はじめに
XCode7で変わった点をまとめます。
といっても、公式なものではなく僕がXcode7にバージョンをあげた時に気づいた事を書いてます。
※Swift2の新しい構文などについては取り上げてません。
Convert
がvar
→let
までしてくれる
僕はこの辺はほとんどやってたつもりでしたが、Convert
に指摘されました。
なので以下のような構文はConvert
が変更してくれます。
func test() {
var hoge = Hoge()
hoge.test()
}
上記のように特にhoge
インスタンスを変更してない場合は
func test() {
let hoge = Hoge() // ←変えてくれる
hoge.test()
}
このように変更してくれます。
コンパイルが早くなった。
コンパイル速度よりも、それに伴ってコード補完が早くなりました。
僕は古いMacBook Air
で開発しているので、これは嬉しい。
ただ、使い続けるとどうなるかはまだ分かりません。
警告が増えた。
今までは使ってない変数があっても警告は出ませんでしたが今回からは警告が出ます。
僕は主に以下のような構文を使ってたのでこれらが警告になりました。
if let foo = hoge as? Foo
// fooは条件を満たすかどうかのためでしか使ってない。
end
これは以下のように変更しないと警告になります。
if let _ = hoge as? Foo
// fooは条件を満たすかどうかのためでしか使ってない。
end
また無駄なweak
に関しても警告がでます。
CallBlock() { [weak self] in
// selfを使ってない。
},
コピペコードでありがちな上記なようなコードにも警告を出してくれます。
助かります。
注意点。
僕はgfxさんのSwiftでマルチスレッド時の同期処理はどうやるのかを使用して同期処理をしてましたがこれは警告が発生してます。
class AutoSync {
let object : AnyObject
init(_ obj : AnyObject) {
object = obj
objc_sync_enter(object)
}
deinit {
objc_sync_exit(object)
}
}
これを以下のように使っていた。
let lock = AutoSync(self)
警告を解除するために
let _ = AutoSync(self)
とやると、その時点でメモリが解放されてしまいスレッドブロックをしてもらえなくなります。
(_
はweak
と同様の扱いのようでretain count
を増やしてくれません。)
なので、適当なメソッドを入れて対応しました。
class AutoSync {
let object : AnyObject
init(_ obj : AnyObject) {
object = obj
objc_sync_enter(object)
}
func end() {}
deinit {
objc_sync_exit(object)
}
}
let lock = AutoSync(self)
...何かの処理
lock.end()
警告がでても無視する方はそのまま使ってもいいかと思います。
countやenumerate、containsが自然な形になった。
もともとcount
やenumerate
はSwift
クラスが提供してくれてたので
count([])
count("test")
contains(["test", "abc"], "test") // "test"が配列にあるか?
for (key, value) in enumerate({"key": value}) {
}
という構文で対応してたのですが、以下のように自然な形になりました。
特にcontains
はどっちがどっちだっけと迷うこともあったので助かります。
[].count
"test".characters.count
["test", "abc"].contains("test") // "test"が配列にあるか?
for (key, value) in {"key": value}.enumerate() {
}
ところでSwift
クラスは無くなったのでしょうか・・・?
あれはオブジェクト指向から逸脱してる特殊クラスだったので廃止されたのかな・・・?
NSMutableDictionary as? [String: AnyObject]ができなくなった。
僕はObjective-c
から受け取ったデータを無理やり[String: AnyObject]
に変換してました。
let objData:NSMutableDictionary = obj.hash()
let dic = objData as! [String: AnyObject]
しかし、これはもう使えなくなってました。
コンパイルエラーが発生するため素直にNSMutableDictionary
のまま処理をすることにしました・・・。
ただ、Objective-cのNSMutableArrayとNSMutableDictionaryをSwiftで使う際にはまったことのNSMutableArray
→NSMutableDictionary
のパターンはうまくできました・・・。
この辺りは謎です。
printlnがなくなりました。
これからはprint
構文のみになります。
convenience initに?
が
コンビニエンスイニシャライズが少し変わりました。
required convenience init(coder aDecoder: NSCoder) {
// 復元処理
}
これが以下のようになります。
required convenience init?(coder aDecoder: NSCoder) {
// 復元処理
}
test(_ value:int? = nil)が警告に
Swift1.2までは以下のように呼びしたい場合に_
が必要でした。
let hoge = Hoge()
hoge.test() // 初期値を与えなくない。
hoge.test(1) // 初期値を与えたい。
これを行うためには以下の定義をする必要がありました。
func test(_ value:Int? = nil) {
// _が必要だった。
}
この_
がない場合はhoge.test(value: 1)
と記載しなければならなかったのですが_
はなくてもOKになりました。
親クラスで定義しているプロトコルを子で定義する必要がなくなりました。
今までは親クラスが定義しているクラスも子で定義する必要がありました。
class Parent: NSObject, NSCoding {
}
class Child: Parent, NSCoding {
}
これがコンパイルエラーになり、以下のように子ではいちいち定義しなくても良くなりました。
class Parent: NSObject, NSCoding {
}
class Child: Parent {
}
逆に省略できなくなりました。
今まではメソッドで使わない変数を以下のように省略できました。
func test([Hoge], foo: Foo) {}
これは以下のようになります。
func test(_: [Hoge], foo: Foo) {}
当たり前といえば当たり前ですが、僕はoverride
する時に度々使ってたので今後気をつけます。
Objective-cのメソッドの返却値がOptionalになった気がする。
まだ全部確認が取れてませんが、今までよりもObjective-c
の呼び出しが硬くなった気がします。
obj-c.parent.addChild(self)
みたいな構文が以下のようになります。
obj-c.parent!.addChild(self)
第一引数を推測してくれるようになった。
もともとSwiftでは以下の構文でした。
func test(arg1:Int, arg2:Int) {}
test(1, arg2: 2)
このように第一引数の名前は省略できます。
しかし、第一引数が省略可能だった場合は名前の省略ができませんでした。
func test(arg1:Int = 0, arg2:Int = 0) {}
test(arg1: 1, arg2: 2) // arg1が省略できない。
Swift2からは上記の場合でも第一引数の省略ができます。(というかしないとコンパイルエラーになります。)
func test(arg1:Int = 0, arg2:Int = 0) {}
test(1, arg2: 2) // arg1は不要
複雑な構文がNGになりました。
複雑な構文をブロック内に記載するとコンパイルエラーになるようになりました。
CallBack = {
$0.items.filter{ $0.type == .A || $0.type == .B }.count
}
このような構文を書くと以下のエラーが発生します。
Expression was too complex to be solved in reasonable time; consider breaking up the expression into distinct sub-expressions
これは以下のように記載します。
CallBack = {
let items = $0.items.filter{ $0.type == .A || $0.type == .B }
return items.count
}
ブロック内で2行以上に渡る場合はreturn
が必須なのでお忘れなく。
Enumのswitch#defaultに警告が出ます。
今までは以下の構文でも警告が出ませんでした。
enum HogeType {
case A
case B
}
switch {
case A:
// 何かの処理
case B:
// 何かの処理
default:
// 何かの処理
}
僕は後々増える予定のEnumをdefault
で拾う予定のコードを書いてたのですが、これは警告が出ます。
普通に使う場合はとても良い変更です。
String#toInt()が廃止に
今までは文字列をInt型に変換する時に以下のように行ってました。
let intValue = "1".toInt()!
これは廃止され
let intValue = Int("1")!
になりました。
Int()がOptional型で返却されるというのに注目です。
(これが自然かどうか微妙ですが。convenience init
に?
がつくようになったため可能になったのでしょう。きっと。※実装を見てもそんなコードは見当たりませんでしたが・・・どうやってるのかな?)
CFBundleIdentifierが$(PRODUCT_BUNDLE_IDENTIFIER)に
info.plistのCFBundleIdentifier
に$(PRODUCT_BUNDLE_IDENTIFIER)
が適用されます。
僕に対したメリットがないですが、、、
ただ、この変更のためかXCode7対応時にCFBundleIdentifier
が消えてしまうという謎の障害に頭を悩ましました。
もし以下のエラーが出た場合は上部にあるBundleIdentifier
が正しいか見てみましょう。
愚かにも僕はこれで相当手こずりました・・・。
iOS7のシミュレーターがなくなりました。
僕のような個人アプリを作ってる人は良いですが、企業でやってる人は注意してください。
XCode7からはiOS7のシミュレーターがなくなってるので実機でしかテストができないです。
ただ、実機テストが無料になったので端末さえあればそんなに問題にはならないと思います。
Pod Installが必要の場合も
僕の場合はcocoaPod
の古いバージョンを使ってたためPod
周りでエラーが出ました。
$ bundle exec gem install cocoapods
でpod
を最新にして
$ pod install
をすれば解決します。
まとめ
警告が増えたのはとても良かったです。
僕は結構硬いコードを書いてたつもりだったのですが、いくつか引っかかってしまったので助かりました。
また、コンパイル速度が上がったのも良い変更です。
XCode7にあげるのはなかなか苦痛を伴いますが、これからSwift2を楽しめるのであげて良かったかなと思います。
あとコンバータが結構優秀で助かりました。
なにも見ずにsave
しても多分問題は発生しません。(Swift1.2の時はそれなりにエラーが発生してた・・・)
おまけ
僕はcocos2d
で作ったプログラムだったので、XCode7にあげた時に結構きっつい対応が必要でした。
とにかくエラーの原因がわからず最後の最後まで以下のエラーに悩まされました。
object file (/libcocos2d.a(OALSimpleAudio.o)) was built for newer iOS version (9.0) than being linked (7.0)
この手のワーニングはもう諦めました・・・
あと、プログラムは動くけれどエラーが出るという現象も発生しました。
こちらに解決策が書かれてましたが、metallib
を作らないわけなので抜本的な解決策ではありません。。。
僕はmetal
を使ってないので問題ないのですが、metal
を使ってる人は注意が必要です。
gitを使ってからやる方が良いです。
忘れてました。
XCode7にあげる前に必ずgit
へコミットしておきましょう!
いざという時に戻せる安心感と、プロジェクトファイルとかが勝手に書き換えられた場合の差分を見たりできます。
僕はgit
にも随分助けられました。
では、Swift2を楽しんできます。