筋肉SwiftプログラマーになるためのARCトレーニング

## はじめに

こんにちは。haranicleです。
2015年。[エンジニアにも筋肉が求められる時代になりました。](http://hrnabi.com/2015/09/28/9411/)
今回は、筋肉Swiftプログラマーになりたい皆様といっしょに、
Swiftの基礎であるARCについてトレーニングしていきましょう。
がんばりましょう。💪💪💪

## 前提
Swift2.1についての記載しています。

## ARCの基礎

### ARCとは

プログラマーが意識していなくても、Swift(のコンパイラ)がARC(Automatic Reference Counting)という仕組みを使ってメモリ領域管理をしてくれます。 
もやしプログラマーが飯を食っていけるのもARCのおかげです。  
ARCは、基本的に以下のルールに基づいてメモリ領域を管理します。
 
* 参照型(クラスのインスタンスとクロージャなど)のみを対象にする
* インスタンスを生成した時にメモリ空間を確保する
* インスタンスが **必要** な間は、メモリ空間を確保し続ける
* インスタンスが **不要** になった時に、メモリ空間を開放する

### 参照カウント

ARCは参照カウントという仕組みを利用して、インスタンスが  **必要** or **不要** を判定しています。
参照カウントは以下のように定義されています。

* インスタンスごとに割り振られる値
* インスタンスを参照(*1)しているプロパティ、定数、変数の個数

Objective-CでARCをOFFにすると、```retainCount```というメソッドで参照カウントを取得できます。

*1 後述しますが、"参照"はstrong参照と呼ばれるもののみが対象です。 

### ARCの挙動

* 新しいインスタンスを作るとARCがメモリ領域を確保する
  * メモリは以下を保持する
    * インスタンスの型
    * ストアドプロパティ(*2)
* インスタンスの参照カウントが0になったら、そのインスタンスを記録しているメモリ領域を開放する
* 開放したメモリ領域は、他の目的に使用することができる

*2 ストアドプロパティ、コンピューテッドプロパティについては、以下を参照してください 
[The Swift Programming Language (Swift 2.1): Properties](https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Properties.html)


### ARCの挙動サンプルコード

The Swift Programming Languageのサンプルコードをベースに、コメントで解説しています。 

```arc_sample.swift
class Person {
    let name: String
    init(name: String) {
        self.name = name
        print("\(name) is being initialized")
    }
    deinit {
        print("\(name) is being deinitialized")
    }
}

var reference1: Person? // == nil (Optionalなのでnilで初期化される)
var reference2: Person? // == nil
var reference3: Person? // == nil

reference1 = Person(name: "John Appleseed") // John Appleseedが作られる

// "John Appleseed is being initialized"とprintされる

// reference1がJohn Appleseedへの参照を持つためJohn Appleseedの参照カウントが1になる
// John Appleseedは、参照カウントが0でないので開放されない

reference2 = reference1 // reference2はJohn Appleseed を参照している(参照カウント2)
reference3 = reference1 // reference3はJohn Appleseed を参照している(参照カウント3)

reference1 = nil // reference1は何も参照しない(参照カウント2)
reference2 = Person(name: "haranicle") // reference2 が他のインスタンスを参照するようになった(参照カウント1)

// John Appleseedは、参照カウントが1以上なのでdeallocされない

reference3 = nil // reference3は何も参照しない(参照カウント0)

// "John Appleseed is being deinitialized"とprintされる

// John Appleseedの参照カウントが0になったので開放された

do {
    let reference4:Person = Person(name: "Kazushi Hara") // reference4はKazushi Haraを参照している(参照カウント1)
    
    // ここでスコープ抜ける
}
// "Kazushi Hara is being deinitialized"とprintされる
// reference4がなくなるのでKazushi Haraの参照カウント0になり、開放された
```

## ARCの罠 "循環参照"

### 循環参照とは

* 複数のインスタンスが互いに参照をもつとき、参照カウントが0にならず、インスタンスが開放されない **循環参照** という現象を起こすことがあります。
* 循環参照は、プログラマーの筋肉で解決してあげる必要があります。

### 循環参照のサンプルコード

The Swift Programming Languageのサンプルコードをベースに、コメントで解説しています。 

```strong_reference_cycle_sample.swift
class Person {
    let name: String
    init(name: String) { self.name = name }
    var apartment: Apartment?
    deinit { print("\(name) is being deinitialized") }
}

class Apartment {
    let unit: String
    init(unit: String) { self.unit = unit }
    var tenant: Person?
    deinit { print("Apartment \(unit) is being deinitialized") }
}

var john: Person?
var unit4A: Apartment?

john = Person(name: "John Appleseed") // johnの参照カウント1
unit4A = Apartment(unit: "4A")        // unit4Aの参照カウント1

john!.apartment = unit4A // unit4Aの参照カウント2 (*事件現場A)
unit4A!.tenant = john    // johnの参照カウント2 (*事件現場B)

john = nil   // johnの参照カウント1
unit4A = nil // unit4Aの参照カウント1

// 何もprintされない

// ＿人人人人人人人人人人人人人人人人人人＿
// ＞　johnとunit4Aが開放されない！！　＜
// ￣Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^￣
```

## 循環参照の解決方法

### strong参照

今まで、何度も"参照"というワードを使ってきましたが、厳密にはstrong参照と呼ばれるものを指しています。
strong参照は、以下のように記述します。

```
strong var john = Person(name: "John Appleseed")

// Swiftではデフォルトでstrong参照になるため、"strong"は省略できる
var haranicle = Person(name: "Kazushi Hara")
```

前述しましたが、strong参照するとインスタンスの参照カウントが+1されます。 　  
循環参照のサンプルコードの事件現場Aか事件現場Bで参照カウントが増えなければ、今回のような悲惨な結果には、ならなかったはずです。

Swiftは、参照カウントが増えない(参照カウントのカウント対象にならない)参照の方法を2つ提供しています。

### weak参照

weak参照は以下のような特徴があります。

* 参照しても、参照カウントが増えない
* **参照先がnilになることがある**
  * Optional型
* 定数で使用することはできない
  * 使おうとしたらXcodeで以下のようなエラーが出ます<br/>'weak'must be a mutable variable, because it may change at runtime  

#### 使用するパターン

以下のようなパターンで使います。
* AはBの参照をもつことができる
* BはAの参照をもつことができる

### weak参照のサンプルコード

The Swift Programming Languageのサンプルコードをベースに、コメントで解説しています。 

```weak_reference_sample.swift
class Person {
    let name: String
    init(name: String) { self.name = name }
    var apartment: Apartment?
    deinit { print("\(name) is being deinitialized") }
}

class Apartment {
    let unit: String
    init(unit: String) { self.unit = unit }
    weak var tenant: Person? // weakになっている！
    deinit { print("Apartment \(unit) is being deinitialized") }
}

var john: Person?
var unit4A: Apartment?

john = Person(name: "John Appleseed") // johnの参照カウント1
unit4A = Apartment(unit: "4A") // unit4Aの参照カウント1

john!.apartment = unit4A // unit4Aの参照カウント2
unit4A!.tenant = john // johnの参照カウント1

john = nil // johnの参照カウント0

// John Appleseed is being deinitializedとprintされる

unit4A = nil // unit4Aの参照カウント0

// Apartment 4A is being deinitializedとprintされる
```

### unowned参照

unowned参照は以下のような特徴があります。

* 参照しても、参照カウントが増えない
* **参照先がnilになることはない**
  * Optional型ではない
  * 参照先が解放済みのとき、unowned参照のプロパティ、変数にアクセスするとランタイムエラーでアプリがクラッシュする
  * 絶対に参照先が開放されていない自身があるときはunownedを使用する
* 定数で使用することはできない
  * 使おうとしたらXcodeで以下のようなエラーが出ます<br/>'weak'must be a mutable variable, because it may change at runtime 
  
#### 使用するパターン

以下のようなパターンで使います。
* AはBの参照をもつことができる
* BはAの参照が必要


### unowned参照のサンプルコード

The Swift Programming Languageのサンプルコードをベースに、コメントで解説しています。 

```unowned_reference_sample.swift
class Customer {
    let name: String
    var card: CreditCard?
    init(name: String) {
        self.name = name
    }
    deinit { print("\(name) is being deinitialized");}
}

class CreditCard {
    let number: UInt64
    unowned let customer: Customer // unownedになっている！
    init(number: UInt64, customer: Customer) {
        self.number = number
        self.customer = customer // initでcustomerをセットするので、CreditCardのインスタンスが生きている間にcustomerがnilになることはない
    }
    deinit { print("Card #\(number) is being deinitialized");}
}

var john: Customer? = Customer(name: "John Appleseed") // johnの参照カウントは1

john!.card = CreditCard(number: 1234_5678_9012_3456, customer: john!) // johnの参照カウントは1, cardの参照カウントは1

john = nil // johnの参照カウントは0, cardの参照カウントは0

// John Appleseed is being deinitializedとprintされる
// Card #1234567890123456 is being deinitializedとprintされる
```

### unowned参照とImplicitly Unwrapped Optionalプロパティ

Implicitly Unwrapped Optionalは、アクセスするときにnilだったらランタイムエラーでアプリがクラッシュするプロパティ、変数のことです。
Optional型ではないため、Unwrapせずに値を使用することができます。 
詳しくは、以下を参照してください。 
[The Swift Programming Language (Swift 2.1): The Basics](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/TheBasics.html) 

#### 使用するパターン

以下の様なパターンが発生することがあります。ほとんど無いですが。

* AはBの参照が必要
* BはAの参照が必要

### unowned参照とImplicitly Unwrapped Optionalプロパティサンプルコード

The Swift Programming Languageのサンプルコードをベースに、コメントで解説しています。 

```
class Country {
    let name: String
    var capitalCity: City! // Implicitly Unwrapped Optionalなので、解放済みのときにアクセスするとランタイムエラーでアプリがクラッシュ
    init(name: String, capitalName: String) {
        self.name = name
        self.capitalCity = City(name: capitalName, country: self)
    }
}
 
class City {
    let name: String
    unowned let country: Country // unownedなので、解放済みのときにアクセスするとランタイムエラーでアプリがクラッシュ
    init(name: String, country: Country) {
        self.name = name
        self.country = country
    }
}
```


## Closureと循環参照

Closureも参照型なので、循環参照が発生することがあります。 
Closureで循環参照が発生するのは以下のパターンです。

1. HogeクラスのインスタンスにClosureへのstrong参照のプロパティがある
2. 1.のClosure内で、Hogeクラスのプロパティ、メソッドにアクセスしている

### Closureで循環参照サンプルコード

The Swift Programming Languageのサンプルコードをベースに、コメントで解説しています。 


```closure_sample.swift
class HTMLElement {
    
    let name: String
    let text: String?
    
    // プロパティでasHTMLへのstrong参照を持っている
    lazy var asHTML: Void -> String = { // nameとtextを使いたいからlazy
        // Closure内でselfのプロパティをキャプチャしている
        if let text = self.text {
            return "<\(self.name)>\(text)</\(self.name)>"
        } else {
            return "<\(self.name) />"
        }
    }
    
    init(name: String, text: String? = nil) {
        self.name = name
        self.text = text
    }
    
    deinit {
        print("\(name) is being deinitialized")
    }
}

```

### 筋肉クイズ1

突然ですが、筋肉クイズの時間です。 
closure_sample.swiftのクラス定義がある時...

```closure_sample_quiz1.swift
var heading:HTMLElement? = HTMLElement(name: "h1")
let defaultText = "some default text"
heading!.asHTML = {
    return "<\(heading!.name)>\(heading!.text ?? defaultText)</\(heading!.name)>"
}
print(heading!.asHTML())

// <h1>some default text</h1>とprintされる

heading = nil

// ★問題★ ここではどうなるでしょう？
// (A) h1 is being deinitializedとprintされる
// (B) なにもprintされない

```

### 筋肉クイズ2

closure_sample.swiftのクラス定義がある時...

```closure_sample_quiz2.swift
var paragraph: HTMLElement? = HTMLElement(name: "p", text: "hello, world")
print(paragraph!.asHTML())

// <p>hello, world</p>とprintされる

paragraph = nil

// ★問題★ ここではどうなるでしょう？
// (A) p is being deinitializedとprintされる
// (B) なにもprintされない
```

解答はキャプチャリストの後で


### キャプチャリスト

Swiftではキャプチャリストという文法で、Closure内で使用するプロパティ、定数、変数のキャプチャの仕方(weak, unowned)を指定することができます。
詳しくは、以下を参照してください。
[The Swift Programming Language (Swift 2.1): Expressions](https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Expressions.html#//apple_ref/doc/uid/TP40014097-CH32-ID544)

#### 引数、戻り値がある場合

```capturelist_sample1.swift
lazy var someClosure: (Int, String) -> String = {
    [unowned self, weak delegate = self.delegate!, userData] (index: Int, stringToProcess: String) -> String in
    // Closure内の処理
}
```

#### 引数、戻り値がない場合

```capturelist_sample2.swift
lazy var someClosure: Void -> String = {
    [unowned self, weak delegate = self.delegate!, userData] in
    // Closure内の処理
}
```

### 筋肉クイズ1の解答

答えは...(A) p is being deinitializedとprintされる。  
原因はよくわかってないです...(だれか教えて下さい)


キャプチャリストを使って以下のように書き換えると、何もprintされなくなります。

```closure_sample_quiz1_answer.swift
heading!.asHTML = {
    [heading] in
    return "<\(heading!.name)>\(heading!.text ?? defaultText)</\(heading!.name)>"
}
```

@_tid_ さんがBox化されているという解釈で説明してくれました！ 
[Swift - クロージャのメモリ管理について - Qiita](http://qiita.com/_tid_/items/df7f1b76f156bd758c1a)

### 筋肉クイズ2の解答

答えは...(B) なにもprintされない
asHTMLのイニシャライザでセットされているクロージャ内でselfがキャプチャされるためです。

HTMLElementのプロパティasHTMLを以下のように書き換えると、p is being deinitializedとprintされるようになります。

```closure_sample_quiz2_answer.swift
lazy var asHTML: Void -> String = {
    [weak self] in
    if let text = self.text {
        return "<\(self.name)>\(text)</\(self.name)>"
    } else {
        return "<\(self.name) />"
    }
}
```

## 言いたかったこと

(2015/12/18 追記)

* 以下の2つの条件を満たすと循環参照する可能性がある
  * プロパティにクロージャを持つ
  * クロージャ内でプロパティの持ち主を参照する
* コーディング(コードレビュー)している時にどのパターンで循環参照するか考えるのは大変すぎる
* プロパティのクロージャからプロパティの持ち主を参照するときは必ず、キャプチャリストでweakかunownedをつけていると安心できる
* コーディング規約で縛ろう💪

### 筋肉コーディング規約

あなたの筋肉をつかって、チームのコーディング規約に以下をコピペしましょう 
('を`に置換してください)

```
## キャプチャリスト

循環参照の発生を防ぐために、プロパティにClosureを宣言するときは、 
キャプチャリストで'''weak'''または'''unowned'''を指定すること💪💪💪

### Closure内でselfを参照する場合

'''
class HTMLElement {
    // (中略)
    lazy var asHTML: Void -> String = {
        [weak self] in // ←💪
        if let text = self!.text {
            return "<\(self!.name)>\(text)</\(self!.name)>"
        } else {
            return "<\(self!.name) />"
        }
    }
}
'''
 
プロパティのClosureのasHTMLがselfを参照するので'''[weak self]'''

### Closureの所有者とClosureの参照先が同じとき

'''
var paragraph: HTMLElement = HTMLElement()
paragraph.asHTML = { [weak paragraph] in // ←💪
	guard let paragraph = paragraph else  {
		return ""
	}
	return "<\(paragraph.name)>\(paragraph.text)</\(paragraph.name)>"
}
'''

paragraphがasHTMLを参照し、asHTMLがparagraphを参照するので'''[weak paragraph]'''

```

## 参照カウントについてもっと詳しく知りたい人へ

参照カウントの実装は、このあたりにありそうです。

[swift/HeapObject.cpp at master · apple/swift · GitHub](https://github.com/apple/swift/blob/master/stdlib/public/runtime/HeapObject.cpp)

## おわりに

これであなたも立派な筋肉Swiftプログラマーです。
明日から、ダンベル片手に出社しましょう。