UIView などでインスタンス単位で一度だけ実行したい初期化コードの考察

## 問題点の整理
`UIView` のサブクラスを書いている時などで初期化の為に一回だけ処理したい場合があったとします。例えば、`UIViewController` を使うときは、 `viewDidLoad()` に色々な初期化の為のコードをかけば、`view` が生成されてから一度しか呼ばれません。よって、`UIViewController` を使っている場合には、ここで、Gesture Recognizer を生成登録したり、複雑なサブビューをコードで生成したりする場合は非常に便利です。

```.swift
class MyViewController: UIViewController {

	func viewDidLoad() {
		super.viewDidLoad()
		// さまざまな初期化のコード
	}
}
```

ところが、さまざまな大人の事情により、これらと同等なコードを `UIView` のサブクラスに持ってくる場合には、どうすればよいでしょうか。

## イニシャライザに初期化のコードを書く

真っ先に思い出すのが、`init` で初期化する方法です。ところが、`init` は複数のイニシャライザが存在する可能性が発生するので、その場合重複したコードを二度書くのは、なんかいただけないです。

```.swift
class MyView: UIView {
	override init(frame: CGRect) {
		super.init(frame: frame)
		// さまざまな初期化のコード
	}

	required init?(coder: NSCoder) {
		super.init(coder: coder)
		// さまざまな初期化のコード
	}
}
```

では、`setup()` という関数でも用意して、それぞれのイニシャライザから呼び出せばいいと思うと以下のようなコードになります。

```.swift
class MyView: UIView {

	override init(frame: CGRect) {
		super.init(frame: frame)
		setup()
	}

	required init?(coder: NSCoder) {
		super.init(coder: coder)
		setup()
	}

	func setup() {
		// さまざまな初期化のコード
	}
}
```

これで妥協するのもありなのですが、MyView のサブクラスの存在を考えると、状況は変化します。

```.swift
class MySubview: MyView {

	init(frame: CGRect, state: Bool) {
		super.init(frame: frame)
		// setup()
	}
	
	required init?(coder: NSCoder) {
		super.init(coder: coder)
		// setup()
	}

	override func setup() {
		super.setup()
		// MySubview固有の初期化のコード
	}

}
```

サブクラスも当然同様な初期化が必要と仮定すると、スーパークラス、サブクラスそしてクラス構造全体で初期化のルールが必要となります。この場合は、サブクラスの `init` では `setup()` を呼び出してはいけません。スーパークラスが呼び出すので、複数呼ばれてしまう事になります。

## `layoutSubviews()` 内に初期化コードを書く

やはり、あるクラスの初期化は、そのスーパークラスやサブクラスの事を心配しないで、自分のクラスだけに注力してコードが書ける方が、良いような気がします。それに、`init` の時って 実際の `window` 下の `view` の階層の中にまだいなかったり、初期化の順番によってはまだ、必要なコンポーネントが準備できていなかったりする場合があり、late initializer の方が都合が良い場合もありますね。そこで、よく使われるのが、`layoutSubviews()` 内に初期化のコードを書く方法です。

```.swift
class MyView: UIView {
	override func layoutSubviews() {
		super.layoutSubviews()
		// さまざまな初期化のコード
	}
}
```

`layoutSubviews()` 内に初期化のコードを記述すれば、イニシャライザがいくつあっても気にする必要がなく、表示前に必ず呼ばれるので重宝します。`backgroundColor` プロパティの設定など、何度呼ばれても気にならないコードはここにダラダラとかけば良いのですが、何度も呼ばれると不都合な場合には一工夫必要となります。`dispatch_once` を使う方法も考えられますが…

```.swift
class MyView: UIView {
	private var token: dispatch_once_t  = 0

	override func layoutSubviews() {
		super.layoutSubviews()

		dispatch_once(&token) {
			// さまざまな初期化のコード
		}
	}
}
```

ドキュメントには以下のような記述があります。`token` は global または static のスコープにおきなさいとあるのでやはり不安です。

> **Declaration**
`void dispatch_once( dispatch_once_t *predicate, dispatch_block_t block);`
> (snip)
> The predicate must point to a variable stored in global or static scope. The result of using a predicate with automatic or dynamic storage (including Objective-C instance variables) is undefined.

もちろん、ここまできたら、`dispatch_once_t` を `Bool` に置き換えて一回だけ初期化するコードを書いてもいいかもしれませんが、もう少し考えてみる事にします。

## 遅延評価を使って考えてみる

なんか `lazy` キーワードを使えば、うまくインスタンスに一回だけ初期化できるコードが書けそうな気がします。

```.swift
class MyView: UIView {

	override func layoutSubviews() {
		super.layoutSubviews()
		if setup {}
	}

	private lazy var setup: Bool = {
		// さまざまな初期化のコード
		return true
	}()
}
```

ちょっと `if` のくだりが、カッコ悪いですが、もう一工夫すれば、カッコ良い方法があるかもしれません。ちなみに、`Bool` を `Void` にしてみると、`if` なんて表現書かなくて良くなるかなと思うと、呼び出す方法が見つかりません。そもそも、`Void` を遅延評価するというのは、想像しがたいものです。


```.swift
	private lazy var setup: Void = {
		// さまざまな初期化のコード
	}()

	override func layoutSubviews() {
		super.layoutSubviews()
		setup // error
	}
```

さらに `()->()` で試してみると、毎度呼ばれてしまいます。

```.swift
	lazy var setup: ()->() = {
		// さまざまな初期化のコード
	}

	override func layoutSubviews() {
		super.layoutSubviews()
		setup()
	}
```

## まとめ

惜しいですが、現時点では良しとしましょう。誰か改善案があれば、よろしくお願いいたします。という事で、Gesture Recognizer を初期化コードとして仕込んでみましょう。（EDIT: 追記があります）

```.swift
class MyView: UIView {

	override func layoutSubviews() {
		super.layoutSubviews()
		if setup {}
	}

	private lazy var setup: ()->() = {
		let panGestureRecognizer = UIPanGestureRecognizer(target: self, action: "panGesture:")
		self.addGestureRecognizer(panGestureRecognizer)
	}

	func panGesture(gesture: UIPanGestureRecognizer) {
		// ジェスチャーの処理
	}
}
```

ちなみに、このやり方で `MyView` のサブクラス化してみましょう。`MyView` のサブクラス `MySubview` を同様に宣言します。

```.swift
class MyView: UIView {

	override func layoutSubviews() {
		print("MyView: \(__FUNCTION__)")
		super.layoutSubviews()
		if setup {}
	}

	private lazy var setup: Bool = {
		print("MyView: \(__FUNCTION__)")
		return true
	}()
}

class MySubview: MyView {

	override func layoutSubviews() {
		print("MySubview: \(__FUNCTION__)")
		super.layoutSubviews()
		if setup {}
	}

	private lazy var setup: Bool = {
		print("MySubview: \(__FUNCTION__)")
		return true
	}()
}
```

このコードで、MySubview を回転し続けますが、 setup はそれぞれ一度しか呼ばれませんでした。

```.console
MySubview: layoutSubviews()
MyView: layoutSubviews()
MyView: setup
MySubview: setup
MySubview: layoutSubviews()
MyView: layoutSubviews()
MySubview: layoutSubviews()
MyView: layoutSubviews()
MySubview: layoutSubviews()
MyView: layoutSubviews()
```

## EDIT: 追記

[rizumitaさん](http://qiita.com/rizumita) さんのコメントにより

[インスタンス単位で一度だけ実行したい初期化コードの考察](http://qiita.com/rizumita/items/62948cc70e83bcca4136)

```.swift
class MyView: UIView {
	lazy var setup: (()->())? = {
		// さまざまな初期化のコード
		self.setup = nil
	}

	override func layoutSubviews() {
		super.layoutSubviews()
		setup?()
	}
}
```

ポイントは、setup の返す値が、`(()->())?` と closure の optional を返す点にあります。なるほど、これで、`self.setup = nil` が少々気になるにしても、 `Bool`を返して、意味不明の `if` 文を入れる必要は無くなりました。そっかぁ、optional かぁ。

って考えると、さらに想像力が湧いてきました。`self.setup = nil` のところを、`return {}()` でかけないかなぁと…

```.swift
	private lazy var setup: (()->())? = {
		// さまざまな初期化のコード
		print("\(__FUNCTION__)")
		return {}()
	}

```

ダメです。結果は、毎回呼ばれてしまいます。そこで気がつきます。あれ？ `setup` の `{}` の後の `()` がないなと…そこで、`setup` の `{}` の後に `()` をつけると、`return` の `{}` の後の `()` が不要になります。これでは、体調が悪いと簡単に騙されそうですね。気を取り戻して、実行してみるとちゃんと一度しか呼ばれません。

## EDIT: 追記その２

[takasek](http://qiita.com/takasek) さんより改善案をいただきました。Ａ案的なやり方は研究したつもりでしたが、`return {}` ができるのは想定外でした。テストしてみるとちゃんと一度しか呼ばれません。素晴らしい。Ｂ案は takasek さん一押しだそうです。私はどちらもいいように思います。


## 現時点でのまとめ（追記２反映済）

（Ａ案）

```.swift
class MyView: UIView {

	override func layoutSubviews() {
		print("MyView: \(__FUNCTION__)")
		super.layoutSubviews()
		setup()
	}

	private lazy var setup: (()->()) = {
		print("MyView: \(__FUNCTION__)")
		// さまざまな初期化のコード
		return {}
	}()
}
```

（Ｂ案）

```.swift
class MyView: UIView {

	override func layoutSubviews() {
		print("MyView: \(__FUNCTION__)")
		super.layoutSubviews()
		setup?()
	}

	private lazy var setup: (()->())? = {
		print("MyView: \(__FUNCTION__)")
		// さまざまな初期化のコード
		return nil
	}()
}
```


```.console
MyView: layoutSubviews()
MyView: setup
MyView: layoutSubviews()
MyView: layoutSubviews()
MyView: layoutSubviews()
```

[rizumitaさん](http://qiita.com/rizumita)の closure を optional にするアイディアがなければ、ここにたどり着きませんでした。ありがとうございました。
 
[takasekさん](http://qiita.com/takasek)の 改善案も納得で採用させていただきます。ありがとうございました。