Swift で Class-Only Protocol を定義する

通常 Swift で `protocol` を定義した場合、その `protocol` は `class` のみならず `struct` や `enum` などにも適用できてしまいます。ほとんどの場合これは非常に便利ですが、時々逆に不便になることもあります。例えば Delegate を `protocol` で定義した際、値型の `struct` などにも使えるため、`var delegate = Delegate?` が書けても `weak var delegate = Delegate?` が書けません。値型に `weak` がそもそも意味ないからです。もちろん Delegate を `struct` にするという手もありますが、Delegate の性質上、参照型の方が嬉しいという場面が非常に多いです、例えば MVC アーキテクチャで `UIViewController` を Model の Delegate にしたいときとかまさにそれです。そういった場合、あらかじめこのプロトコルは `class` だけにしか使えないとわかっていても、通常の `protocol` の宣言の仕方ではそれがわからないため、`weak` が使えずメモリリークにつながりかねません。

例えば下記のようなソースコード見てみればわかると思います：

```swift:before
protocol ADelegate {
	
}

class A {
	var delegate: ADelegate?
	
	deinit {
		print("A deinited")
	}
}

class P {
	let child: A
	init(child: A) {
		self.child = child
	}
	deinit {
		print("P deinited")
	}
}

extension P: ADelegate {
	
}

var a: A? = A()
var p: P? = P(child: a!)

a!.delegate = p

p = nil
a = nil
```

上記のコードを Playground で実行してみればわかると思いますが、`A` も `P` も `deinit` が呼び出されていません。そのつもりはなかったが結局循環参照が起こってしまうため `a!.delegate` が参照カウンターを保持しており結局 `a` も `p` もメモリが解放されません。これを解決するには `a = nil` の前に `a!.delegate = nil` を入れないといけません。でもこれだと面倒だし忘れがちだしあまりいい解決法とは言い難いですね。

というわけで、ここはこの `delegate` は参照型だと最初から分かっている場合、やはり `weak` を使いたいのですが、ここで登場してもらいたいのがこの Class-Only Protocol です。

実は Class-Only Protocol の宣言は非常に簡単で、定義した `protocol` に `class` の継承を付け加えるだけで OK です。つまりこう：

```swift
protocol ADelegate: class {
	
}
```

です。こうすることによって、`ADelegate` は `class` のみに適用される `protocol` とみなされ、当然 `ADelegate` の変数も `weak` が使えるようになります。

これで最初のコードをちょっと修正してみましょう：

```swift:after
protocol ADelegate: class {
	
}

class A {
	weak var delegate: ADelegate?
	
	deinit {
		print("A deinited")
	}
}

class P {
	let child: A
	init(child: A) {
		self.child = child
	}
	deinit {
		print("P deinited")
	}
}

extension P: ADelegate {
	
}

var a: A? = A()
var p: P? = P(child: a!)

a!.delegate = p

p = nil
a = nil
```

これでもう一度 Playground で実行してみると、`deinit` がちゃんと両方の `class` に呼び出されたことが確認できます。めでたしめでたし。

ちなみにもう一つ嬉しいのは、参照型だとわかったから、`protocol` には `mutating func` の宣言が要らなくなります。すべて `func` のみで OK ですので、これで `mutating func` のメソッドが入ってる `protocol` を `class` で対応した時も変数は `var` 使わなければなりません、という問題が根本的に解決されます。例えば下記のようなものがあるとします：

```swift:before
protocol SomeProtocol {
	var aVariable: Int { get set }
	mutating func increase()
}

extension SomeProtocol {
	mutating func increase() {
		self.aVariable += 1
	}
}

class SomeClass: SomeProtocol {
	var aVariable: Int
	init() {
		self.aVariable = 0
	}
}

let some = SomeClass()
some.increase()
```

ここで `increase()` は `self.aVariable` の値を変更しているのでどうしても `mutating func` で宣言しないといけませんが、そのせいで本来 `let` で宣言できた `some` が `increase()` を使いたい場合 `var` で宣言しないといけなくなるのでこのコードではエラーが出ます。これも `SomeProtocol` を Class-Only Protocol として宣言すれば解決されます：

```swift:after
protocol SomeProtocol: class {
	var aVariable: Int { get set }
	func increase()
}

extension SomeProtocol {
	func increase() {
		self.aVariable += 1
	}
}

class SomeClass: SomeProtocol {
	var aVariable: Int
	init() {
		self.aVariable = 0
	}
}

let some = SomeClass()
some.increase()
```

ちなみに C# では Abstract Class（抽象クラス）という、いわば `protocol` と非常に似ているものがあって、要するに `class` のようにプロパティやメソッドなどの宣言があるがその定義や実現は一切せず、すべてはこの Abstract Class を継承した子クラスに任せており、当然ながら Abstract Class 自身のインスタンスも作ることができません。Swift では Abstract Class と言う概念がありませんが、Class-Only Protocol を使えばある意味 Abstract Class とほぼ同じようなものが作れるのではないでしょうか。

というわけで、もし `class` 専用の `protocol` を作りたい場合は、ぜひこの Class-Only Protocol の宣言を活用しましょう。

参考：https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Protocols.html#//apple_ref/doc/uid/TP40014097-CH25-ID281

---
# 2018/11/06 追記
[The Swift Forums](https://forums.swift.org/t/class-only-protocols-class-vs-anyobject/11507#post_11) によりますと、今後は `: class` が非推奨となり、代わりに `: AnyObject` の方が推奨となるそうです、なぜならば実際欲しい動きは「クラスに限定」よりも、「参照型に限定」ですので：

```swift
protocol SomeProtocol: AnyObject {
    // ...
}
```