https://developer.apple.com/swift/ より
The Swift Programming Language (iBooks Store)をざっくり目を通してつらつらと。
ARC (Autometic Reference Counting)
Swift では Objective-C で採用された ARC という仕組みでメモリ管理を行います。
ARC についてはざっくり……
- 参照渡しのオブジェクトは strong が基本
- 循環参照 (相互参照) になるオブジェクトに関しては weak
- optional ではないオブジェクトに対して weak を利用したい場合は unowned
- クロージャで参照されるオブジェクトはクロージャ内部で strong
- なので、クロージャで
self
が循環参照になってしまうような場合にはクロージャ直前に[unowned self]
と宣言してクロージャ内部でself
を strong にならないようにする
Optional Chaining
任意型変数 (Optinal Value)
Swift では、変数や定数に対して任意型 (Optional) として宣言することができます。
var isEnabled: Bool? = true
型の後に続けて、?
をつけると任意型になります。
Bool
な変数は true
か false
を取りますが、Bool?
な変数はそれに加えて、値なし (No Value) な nil
を取ります。
任意型な変数に対して初期値を設定しない場合、初期値は nil
になります。
var isEnabled: Bool? // isEnabled は nil
また、任意型な変数を if
などの条件式で検査した場合は nil
かどうかを判断する挙動になることに注意。
例えば、次のようなコードを実行すると
var isEnabled: Bool? = false // isEnabled は false
if isEnabled {
println("What!?") // 実行される
}
isEnabled
は false
で nil
ではないので、if
の条件式は「真」になり、"What!?" と出力されます。
この時に isEnabled
の値そのもので条件式を実行したい場合、!
をつけて任意形をアンラップ (unwrap) します。
var isEnabled: Bool? = false // isEnabled は false
if isEnabled! {
println("What!?") // 実行されない
}
ただ、nil
な値の変数をアンラップすると、実行時エラーとなるので注意。
var isEnabled: Bool? // isEnabled は nil
if isEnabled! { // エラー
println("What!?")
}
Optinal Chaining
任意型の変数 (プロパティ) に ?
をつけることで、次に続くプロパティやメソッドに値そのものを渡すように動作させることができます (暗黙的に !
がついたような動作になる)。
ただし、!
と異なり、?
は対象が nil
の場合、それ以降の評価を止めるように動作し、実行時エラーにはなりません。
この動作の仕組みを利用してプロパティやメソッドを数珠つなぎのように呼び出すような記述をすることができます。これを Optinal Chaining と呼ぶようです。
例えば、
if let firstName = aPerson.info?.name?.firstName {
println("first name is \(firstName)")
}
というコードでは、
-
aPerson
のプロパティinfo
に値が設定されていたら (nil
ではなかったら)、 -
info
の中のプロパティname
にアクセスし、それがnil
ではなかったら、 -
name
の中のプロパティfirstName
にアクセスして、 - その内容をローカルな定数
firstName
に格納します。
その結果、定数 firstName
の値が if
文に対して評価されます。
したがって、以下のコードとほぼ同義です。
if aPerson.info {
if aPerson.info!.name {
let firstName = aPerson.info!.name!.firstName
if firstName {
println("first name is \(firstName)")
}
}
}
動作サンプル:
struct Address {
var street:String?
var postalCode:String?
}
struct Name {
var firstName:String?
var lastName:String?
}
struct Info {
var name:Name?
var address:Address?
}
class SomeClass {
var info:Info?
init(info:Info?) {
self.info = info
}
}
let name = Name(firstName:"Steve", lastName:"Jobs")
let address = Address(street:"1 Infinite Loop", postalCode:nil)
var aPerson = SomeClass(info:Info(name:name, address:address))
if let firstName = aPerson.info?.name?.firstName {
println("first name is \(firstName)")
} else {
println("No first name")
}