LoginSignup
9
11

More than 5 years have passed since last update.

遅延生成で生成時に色々設定を入れる方法

Last updated at Posted at 2016-04-20

ViewViewController を作る際に、初期化にあまり時間をかけたくないなどといった理由ですぐに使わないけど Optional でない部品を lazy var 使って遅延生成する手法があります。例えば ViewControllerChildViewController を持ちたいけど、実際 ChildViewController が必要になるまで生成したくないといった場合がまさにこうです。

ViewController
import UIKit

class ViewController: UIViewController {

    lazy var childController = ChildViewController()

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        print("Root controller view did load")
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
        print("Touches in root controller ended")
        self.presentViewController(self.childController, animated: true) {
            print("Child controller presented")
        }
    }

}
ChildViewController
import UIKit

class ChildViewController: UIViewController {

    init() {
        super.init(nibName: nil, bundle: nil)
        print("Child controller inited")
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        // Do any additional setup after loading the view.
        print("Child controller view did load")
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

}

この場合、ViewController にタップするまで、ChildViewController の部品は生成されないことをログで見れば一目瞭然です。

Root controller view did load
Touches in root controller ended
Child controller inited
Child controller view did load
Child controller presented

ところが、このような単純な lazy var を使う場合、生成時に色々設定を入れてあげること(backgroundColor を設定してあげたいとか色々)ができません。

じゃあどうすればいいかというと、少なくとも二通りの方法があります。

まず一つ目は非常にレガシーなやり方で Objective-C の時代からある方法です。もう一つ private 変数を使うことです。例えば上記の lazy var childController = ChildViewController() ですと、下記のように書きます:

ViewController
//前略
    private var _childController: ChildViewController?
    var childController: ChildViewController {
        if let controller = self._childController {
            return controller

        } else {
            let controller = ChildViewController()
            self._childController = controller
            controller.view.backgroundColor = .redColor()
            return controller
        }
    }
//後略

このように、一つ private var _childController という変数を作ることによって、lazy var と同じ効果が得られる上、生成時に色々設定をしてあげることも可能です。

もう一つの方法は lazy var をそのまま使うが、クロージャーも一緒に取り入れるという Swift ならではの方法です:

ViewController
//前略
    lazy var childController = {() -> ChildViewController in
        let controller = ChildViewController()
        controller.view.backgroundColor = .redColor()
        return controller
    }()
//後略

両方とも通常の lazy var の時と全く同じように self.childController でそのまま使えますが、クロージャーの方が余計なインスタンス変数を作らずに済むし記述量も少ないのでとてもスマートなコードが書けます。ただし一つだけ制限もありまして、lazy var を使う場合は使われてる時クラスが生成されたことが保証できないため、クロージャーの中に self が使えません。なので self を使った設定は private var _を使うしかなさそうです。コメント情報によりますと、クロージャーの中に self を使いたい場合は型を明示すればできます:

ViewController
//前略
    lazy var childController: ChildViewController = {
        let controller = ChildViewController()
        controller.view.backgroundColor = self.view.backgroundColor()
        return controller
    }()
//後略
9
11
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
11