Swiftに関するメモ その2


* [
Swiftに関するメモ - 一覧](http://qiita.com/takkyun/items/f34abf857deed808c610)

https://developer.apple.com/swift/ より

[The Swift Programming Language (iBooks Store)](https://itunes.apple.com/us/book/the-swift-programming-language/id881256329?mt=11)をざっくり目を通してつらつらと。

[前回](http://qiita.com/takkyun@github/items/080a81a43111387c01fd)は `class` と `struct` に触れたところまで。今回はそこから。

まだまだコード書いてません。

# class と struct
## 宣言と利用

```swift
class SomeClass {
  var value = 0 // インスタンス変数 value で初期値 = 0
  // ... 中略 ...
}
```

```swift
struct SomeStructure {
  // ... 中略 ...
}
```

特に問題ないかな。クラス名や構造体名には UpperCamelCase を利用して、メソッドやプロパティ(変数)には lowerCamelCase を利用するのが作法のようです。ここら辺は Cocoa Framework からの流儀になるのかな。

`class`や`struct`のインスタンスを作るのに new なんかは要りません。

```swift
let instanceOfSomeClass = SomeClass()
```

となります。変数にアクセス (setter & getter) には `.` を使う。

```swift
instanceOfSomeClass.value // => 0
instanceOfSomeClass.value = 1 // => 1
```

private とか protected やらプロパティとかメソッドの公開範囲をしているようなものはないのかな。全てが public になるんでしょうか。

フレームワークとかで公開するインタフェースとそうじゃない部分を分けたりするのはどうしたらいいんだろう。

# 値渡し (Value Type) と参照渡し (Reference Type)

`struct` と `enum` は Value Types と呼ばれる仕組みで動作します。

```swift
let sample1 = SomeStructure(value: 0)
var sample2 = sample1
```

というコードがあったとき、`sample1.value`と`sample2.value`の値はこの時点で同じです。

```swift
sample2.value = 1
```

とすると、`sample2` の `value` だけが変わり、`sample1.value` には影響しません。

これが同じようなことを `class` でやると動作が変わってきます。

```swift
let sample1 = SomeClass(value: 0)
let sample2 = sample1
sample2.value = 1
```

`class` は Reference Type で参照渡しで動作します。

この例で `sample1` と `sample2` は違う名前ですが、同じインスタンスを参照していることになります。

したがって

```swift
sample1.value // => 1
```

となります。

## 参照渡しの同一性

`===` もしくは `!==` で Referece Type のオブジェクト (`class` のインスタンス) の同一性を確認することができます。

```swift
let sample1 = SomeClass(value: 0)
let sample2 = sample1
let sample3 = SomeClass(value: 0)

sample1 === sample2 // => true
sample1 === sample3 // => false, でも sample1 == sample3 が true である可能性はある
```

## 配列とディクショナリの代入・コピー

前回触れたディクショナリは値渡しになるようです。

```swift
var test1 = ["key1": "val1", "key2": "val2"]
var test2 = test1
test2["key2"] = "val2+" // test1["key2"] は "val2" のまま
```

<strike>配列は少し特殊で一見参照渡し風なんですが、配列の長さが変わるような操作を行うとその時点でコピーが行われます。</strike>

(2014.07.08 追記) 「[SwiftのArrayがヤバくなくなった](http://qiita.com/aki/items/6c68a69de0c05cb174ea)」で触れられているとおり、beta 3 より挙動が変わりました。

```swift:beta2以前の挙動
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
```

```swift:beta3以後の挙動
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
```

<strike>ここまでは参照渡し風で元の `array1` の値が変われば `array2` も `array3` も変わります。 </strike>

このとき、`array1` の長さが変わるよう操作を行うと……

```swift:beta2以前の挙動
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 には影響しません。

```swift:beta3以後の挙動
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` メソッドを使えば、コピーが代入されるので、値渡しのような動作をさせることができます。

```swift
var array4 = array1.copy()
```

Swift は Objective-C コードも利用できて配列やディクショナリに `NSArray` や `NSDictionary` が利用できます。これらは `class` なので、常に参照渡しになります。

# プロパティ (Properties)

クラス `class`、構造体 `struct`、列挙型 `enum` 内で宣言した変数はプロパティと呼ばれます。

インスタンス変数 (らしきもの) は代表的なプロパティで、クラス変数も type propeties (タイププロパティ) と呼ばれるプロパティです。

## 格納型プロパティ (stored properties)

基本形：

```swift
struct SomeStructure {
  var value: Int
  let constant: Int
}
```

ここで `value` と `constant` は `SomeStructure` の stored propeties になります。

前述の通り、プロパティには `.` でアクセスします。

```swift
let theStruct = SomeStructure(value: 1, constant: 2)
```

とした場合、`theStruct` は定数で、`struct` は値渡しのため、`value` が変数として定義されていても変更不可能になります。

```swift
let theStruct = SomeStructure(value: 1, constant: 2)
theStruct.value = 2 // <= エラー!!

var aStruct = SomeStructure(value: 1, constant: 2)
aStruct.value = 1 // <= aStruct は変数なので、これは OK
```

`@lazy` アトリビュートをつけることでプロパティの初期化をインスタンスの初期化ではなく、プロパティにアクセスした時点に行うようにすることができます。

```swift
struct SomeStructure {
  @lazy var value = OtherStructure()
  // value はプロパティにアクセスされるまで初期化されない
}
```

`@lazy` アトリビュートは変数だけにしか適用できません。

## 算出型プロパティ (computed properties)

プロパティの呼び出しに応じて出力される値や入力される値を算出するタイプのプロパティを定義することができ、computed properties と呼ばれます。

サンプル：

```swift
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` を省略して定義します。

サンプル：

```swift
struct SomeStructure {
  var x = 0.0
  var y = 0.0
  var middle: Int { // middle は read-only
    return (x + y) / 2
  }
}
```

## タイププロパティ (type properties)

インスタンスではなく、クラスや構造体自体にプロパティを定義することができ、type properties と呼ばれます。

サンプル：

```swift
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`) あたりまでは次になるでしょうか。