https://developer.apple.com/swift/ より
The Swift Programming Language (iBooks Store)をざっくり目を通してつらつらと。
前回は class
と struct
に触れたところまで。今回はそこから。
まだまだコード書いてません。
class と struct
宣言と利用
class SomeClass {
var value = 0 // インスタンス変数 value で初期値 = 0
// ... 中略 ...
}
struct SomeStructure {
// ... 中略 ...
}
特に問題ないかな。クラス名や構造体名には UpperCamelCase を利用して、メソッドやプロパティ(変数)には lowerCamelCase を利用するのが作法のようです。ここら辺は Cocoa Framework からの流儀になるのかな。
class
やstruct
のインスタンスを作るのに new なんかは要りません。
let instanceOfSomeClass = SomeClass()
となります。変数にアクセス (setter & getter) には .
を使う。
instanceOfSomeClass.value // => 0
instanceOfSomeClass.value = 1 // => 1
private とか protected やらプロパティとかメソッドの公開範囲をしているようなものはないのかな。全てが public になるんでしょうか。
フレームワークとかで公開するインタフェースとそうじゃない部分を分けたりするのはどうしたらいいんだろう。
値渡し (Value Type) と参照渡し (Reference Type)
struct
と enum
は Value Types と呼ばれる仕組みで動作します。
let sample1 = SomeStructure(value: 0)
var sample2 = sample1
というコードがあったとき、sample1.value
とsample2.value
の値はこの時点で同じです。
sample2.value = 1
とすると、sample2
の value
だけが変わり、sample1.value
には影響しません。
これが同じようなことを class
でやると動作が変わってきます。
let sample1 = SomeClass(value: 0)
let sample2 = sample1
sample2.value = 1
class
は Reference Type で参照渡しで動作します。
この例で sample1
と sample2
は違う名前ですが、同じインスタンスを参照していることになります。
したがって
sample1.value // => 1
となります。
参照渡しの同一性
===
もしくは !==
で Referece Type のオブジェクト (class
のインスタンス) の同一性を確認することができます。
let sample1 = SomeClass(value: 0)
let sample2 = sample1
let sample3 = SomeClass(value: 0)
sample1 === sample2 // => true
sample1 === sample3 // => false, でも sample1 == sample3 が true である可能性はある
配列とディクショナリの代入・コピー
前回触れたディクショナリは値渡しになるようです。
var test1 = ["key1": "val1", "key2": "val2"]
var test2 = test1
test2["key2"] = "val2+" // test1["key2"] は "val2" のまま
配列は少し特殊で一見参照渡し風なんですが、配列の長さが変わるような操作を行うとその時点でコピーが行われます。
(2014.07.08 追記) 「SwiftのArrayがヤバくなくなった」で触れられているとおり、beta 3 より挙動が変わりました。
var array1 = [1,2,3]
var array2 = array1
var array3 = array1
array1[0] // => 1
array2[0] // => 1
array3[0] // => 1
array1[0] = 123
array1[0] // => 123
array2[0] // => 123
array3[0] // => 123
var array1 = [1,2,3]
var array2 = array1
var array3 = array1
array1[0] // => 1
array2[0] // => 1
array3[0] // => 1
array1[0] = 123
array1[0] // => 123
array2[0] // => 1
array3[0] // => 1
ここまでは参照渡し風で元の array1
の値が変われば array2
も array3
も変わります。
このとき、array1
の長さが変わるよう操作を行うと……
array1.append(4) // array1 = [123,2,3,4]
array1[0] = 1 // array1 = [1,2,3,4]
array1[0] // => 1
array2[0] // => 123 (array1 と違う)
array3[0] // => 123 (array1 と違う)
そもそも最初の代入時点で値渡しされる (copy される) ので、append をしようがしまいが array2 / array3 には影響しません。
array1.append(4) // array1 = [123,2,3,4]
array1[0] = 1 // array1 = [1,2,3,4]
array1[0] // => 1
array2[0] // => 1
array3[0] // => 1
配列で copy
メソッドを使えば、コピーが代入されるので、値渡しのような動作をさせることができます。
var array4 = array1.copy()
Swift は Objective-C コードも利用できて配列やディクショナリに NSArray
や NSDictionary
が利用できます。これらは class
なので、常に参照渡しになります。
プロパティ (Properties)
クラス class
、構造体 struct
、列挙型 enum
内で宣言した変数はプロパティと呼ばれます。
インスタンス変数 (らしきもの) は代表的なプロパティで、クラス変数も type propeties (タイププロパティ) と呼ばれるプロパティです。
格納型プロパティ (stored properties)
基本形:
struct SomeStructure {
var value: Int
let constant: Int
}
ここで value
と constant
は SomeStructure
の stored propeties になります。
前述の通り、プロパティには .
でアクセスします。
let theStruct = SomeStructure(value: 1, constant: 2)
とした場合、theStruct
は定数で、struct
は値渡しのため、value
が変数として定義されていても変更不可能になります。
let theStruct = SomeStructure(value: 1, constant: 2)
theStruct.value = 2 // <= エラー!!
var aStruct = SomeStructure(value: 1, constant: 2)
aStruct.value = 1 // <= aStruct は変数なので、これは OK
@lazy
アトリビュートをつけることでプロパティの初期化をインスタンスの初期化ではなく、プロパティにアクセスした時点に行うようにすることができます。
struct SomeStructure {
@lazy var value = OtherStructure()
// value はプロパティにアクセスされるまで初期化されない
}
@lazy
アトリビュートは変数だけにしか適用できません。
算出型プロパティ (computed properties)
プロパティの呼び出しに応じて出力される値や入力される値を算出するタイプのプロパティを定義することができ、computed properties と呼ばれます。
サンプル:
struct SomeStructure {
var x = 0.0
var y = 0.0
var middle: Int {
get {
return (x + y) / 2
}
set {
x = newValue / 2
y = newValue / 2
}
}
}
get
で getter を、set
で setter を定義します。set
には引数を取りますが、省略した場合、newValue
という引数が set
に渡された引数として利用できます。
読み出しのみ (read-only) のプロパティを定義する場合、get
や set
を省略して定義します。
サンプル:
struct SomeStructure {
var x = 0.0
var y = 0.0
var middle: Int { // middle は read-only
return (x + y) / 2
}
}
タイププロパティ (type properties)
インスタンスではなく、クラスや構造体自体にプロパティを定義することができ、type properties と呼ばれます。
サンプル:
struct SomeStructure {
static var typeProperty = 0
}
enum SomeEnumeration {
static var typeProperty = 0
}
class SomeClass {
class var typeProperty {
return 0
}
}
struct
と enum
、値渡しのこれらのタイプには static
を使います。参照渡しの class
には class
を使います。
追記。
……今日はここまで。プロパティの監視 (didSet
と willSet
) については次の機会に。メソッド・初期化 (init
)・終了化 (deinit
) あたりまでは次になるでしょうか。