Edited at

lazy var ~を覚えてソースをちょっとだけすっきりとできたお話

More than 3 years have passed since last update.

すでに出来上がったアプリに「lazy var~」という記述で変数が宣言されているのを発見し、

どういうものか調べたらちょっとだけすっきりと、

ぬるぽの危険が減るコードが書けるようになった(気がする)。

後から実際に使おうとして「あれ、何だっけなぁ」と思い出すのに少々時間を要してしまったので備忘録。

「lazy var~」の仕様については、以下が大変参考になりましたm(_ _)m

http://www.slideshare.net/tomohirokumagai54/lazy-var-cocoakansai-cswift


Before/After例

CollectionViewやTableViewで項目を複数選択するソースコード(選択時の処理は割愛)。

セル自体にだけ選択ステータスを持たせると、スクロールして

選択済みのセルが隠れた際に選択状態リセットされてしまうので、

ViewController側で現在選択中のセルのインデックスを保持する方法をとる。


Before


ViewController.swift

class ViewController: UIViewController {

/// コレクションビュー
@IBOutlet weak var _collectionView: UICollectionView!

/// 選択セルのインデックス ※ユーザーがセルを選択しないと一切利用されない
var selectedIndices: Array<NSIndexPath>? // = Array<NSIndexPath>() はしたくない

// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~いろいろ省略~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

/** (delegate)セル描画時の処理 */
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("Cell", forIndexPath: indexPath)
// 選択済みセルのインデックス情報にこのセルのインデックス情報が含まれている場合、選択状態にする
if let selections = self.selectedIndices {
for (index, selected) in selections.enumerate() {
if selected == indexPath {
self.selectedIndices!.removeAtIndex(index)
break
}
}
}
return cell
}

/** (delegate)セル選択時の処理 */
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
if let cell = collectionView.cellForItemAtIndexPath(indexPath) {
// 選択状態を切り替える
if cell.selected.boolValue {
// 未選択→選択
// 画面を開いてから最初の選択の場合は、インスタンスの生成を行う
if self.selectedIndices == nil {
self.selectedIndices = Array<NSIndexPath>()
}
self.selectedIndices!.append(indexPath)
} else {
// 選択→未選択
// 選択中インデックス情報からこのセルのインデックス情報を削除
for (index, selected) in self.selectedIndices!.enumerate() {
if selected == indexPath {
self.selectedIndices!.removeAtIndex(index)
break
}
}
}
cell.selected = !cell.selected.boolValue
}
}
}


上記のうちif let selections = self.selectedIndices { ~や、

if self.selectedIndices == nil { self.selectedIndices = Array<NSIndexPath>() }

は、最初から空のインスタンスを変数に入れておけば書かなくて済む。

けれど、個人的に使うことが確定していないインスタンスは作り置きしたくないので

基本使うときになければ新しく作るという書き方をしていた。


After

lazy var~ を利用すると、上記のソースと同じ動きが以下のようになる。


ViewController.swift

class ViewController: UIViewController {

/// コレクションビュー
@IBOutlet weak var _collectionView: UICollectionView!

/// 選択セルのインデックス ※ユーザーがセルを選択しないと一切利用されない
lazy var selectedIndices: Array<NSIndexPath> = {
// クラス生成しただけではnilだけど、
// self.selectedIndices.~など、初めて呼び出した瞬間にインスタンス生成してくれる!
return Array<NSIndexPath>()
}()

// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~いろいろ省略~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

/** (delegate)セル描画時の処理 */
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("Cell", forIndexPath: indexPath)
// 選択済みセルのインデックス情報にこのセルのインデックス情報が含まれている場合、選択状態にする
for (index, selected) in self.selectedIndices.enumerate() {
if selected == indexPath {
self.selectedIndices.removeAtIndex(index)
break
}
}
return cell
}

/** (delegate)セル選択時の処理 */
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
if let cell = collectionView.cellForItemAtIndexPath(indexPath) {
// 選択状態を切り替える
if cell.selected.boolValue {
// 未選択→選択
// 選択中インデックス情報にこのセルのインデックス情報を追加
self.selectedIndices.append(indexPath)
} else {
// 選択→未選択
// 選択中インデックス情報からこのセルのインデックス情報を削除
for (index, selected) in self.selectedIndices.enumerate() {
if selected == indexPath {
self.selectedIndices.removeAtIndex(index)
break
}
}
}
cell.selected = !cell.selected.boolValue
}
}
}


無駄になり得るインスタンス生成処理もしなくて良い。

ぱっと見邪魔なnil考慮の分岐処理も書かなくて良い。

良いこと尽くし。

例のようにインデックス情報だけ保持するため程度だったら

最初から空のインスタンス宣言しても「アプリに無駄な負荷が〜」等の心配はないが、

無駄なものは生成しないための方法の選択肢のレパートリーが

1個増えたのは嬉しいところ。