3

In swift3 document. It is recommended to use lazy by the two following ways:

1. directly init

`lazy var someViews: UILabel = UILabel()`

2. init with block

    lazy var overlayView: UILabel = { [unowned self] in
        let overlayView = UILabel()
        overlayView.backgroundColor = self.backgroundColor.withAlphaComponent(0.90)
        overlayView.font = UIFont.boldSystemFont(ofSize: YFCalendarOverlaySize)
        overlayView.textColor = self.overlayTextColor
        overlayView.alpha = 0.0
        overlayView.textAlignment = .center
        overlayView.translatesAutoresizingMaskIntoConstraints = false
        return overlayView
    }()

If I want to lazily initialize some variable with some default value. I can only use the 2nd way. But that seems too clumsy. So, I use the following method to initialize the lazy var. It runs ok. But is it really okay? I want some help.

class SomeViewController: UIViewController {
    lazy var someViews: UILabel = self.initSomeViews()

    override func viewDidLoad() {
        print(self.someViews)
    }
}

fileprivate extension SomeViewController {

    func initSomeViews() -> UILabel! {
        let overlayView = UILabel()
        overlayView.backgroundColor = UIColor.white.withAlphaComponent(0.90)
        overlayView.font = UIFont.boldSystemFont(ofSize: YFCalendarOverlaySize)
        overlayView.alpha = 0.0
        overlayView.textAlignment = .center
        overlayView.translatesAutoresizingMaskIntoConstraints = false
        return overlayView
    }
}
JeremyP
  • 84,577
  • 15
  • 123
  • 161
Jason Yu
  • 311
  • 3
  • 15
  • _"(...) I can only use the 2nd way. But that seems too clumsy."_, no, actually it is a perfectly reasonable way to do so, while _"So, I use the following method to initialize the lazy var. It runs ok. But is it really okay?"_, that is the clumsy solution here, I'm afraid :( – holex May 04 '17 at 14:00

3 Answers3

2

I advice NOT to use the closure variant:

lazy var overlayView: UILabel = { [unowned self] in
    let overlayView = UILabel()
    // ...
    return UILabel()
}()

Why?

I made a small research myself. Follow this link to read the detailed description.

Proper usage with function:

class SomeViewController: UIViewController {

    lazy var label: UILabel = self.getLabel()
}

fileprivate extension SomeViewController {

    func getLabel() -> UILabel {
        return UILabel()
    }
}
Community
  • 1
  • 1
iWheelBuy
  • 5,470
  • 2
  • 37
  • 71
  • I've read your post, that's good for this problem. So. In this case, is it necessary to use `"[unowned self] in"` in my `"initSomeViews"` function? – Jason Yu May 04 '17 at 13:50
  • @JasonYu unowned is not required. I've updated post with proper usage micro example – iWheelBuy May 04 '17 at 13:56
  • If I use self.title to set the default text in the `getLabel()` method, then is unowned required? If not, where is unowned required? – Jason Yu May 04 '17 at 14:22
  • unowned is not required. But it is a very interesting question about unowned. This question requires a deeper understanding of capture list and etc... – iWheelBuy May 04 '17 at 14:31
  • you should post some other question about unowned/weak and etc – iWheelBuy May 04 '17 at 14:32
  • You don't need to use an `unowned self` capture list in the lazy initialisation closure, as it's executed immediately and not stored. Compare http://stackoverflow.com/q/38141298/2976878 – Hamish May 04 '17 at 14:52
1

Yes thats okay but your initSomeViews() has same concept as of using blocks. You can either directly assign a clouser to it or a method for that.

Note:

If you use your lazy property in viewDidLoad: then there is no need for declaring it as lazy.

-They are initialised just once and never computed again, that is they don't get computed dynamically.

Community
  • 1
  • 1
ankit
  • 3,537
  • 1
  • 16
  • 32
  • Thanks a lot. But would you like to share your **Answer link**? Is your answer part of the swift documentation? – Jason Yu May 04 '17 at 13:38
1

As a matter of safety and style (I probably gonna be downvoted for this…) I savor using implicitly unwrapped optionals for this:

private var someViews: UILabel!

override func viewDidLoad() {
    self.someViews = createSomeViews()
}

private func createSomeViews() -> UILabel { ... }

Safety. Running your initialization at once, on viewDidLoad method, buys you a nice deterministic code path across your view controller setup. Conversely, using lazy you might have more than one code path that triggers the var creation, potentially hiding nasty latent bugs (e.g. think cross-dependencies in your views, etc).

Style. What can I say? It just looks better in the eyes :)

But, if your var initialization contains some costly computation that you want to postpone as much as possible, than lazy is the way to go!

Paulo Mattos
  • 18,845
  • 10
  • 77
  • 85
  • 1
    I don't know why anybody would down vote you on that. It's the same pattern that I use for variables that rely on the user interface having been loaded. You could lazy init them and hope they don't get referenced before the view has loaded, but, as you say, it could introduce nasty non deterministic bugs. – JeremyP May 04 '17 at 13:39
  • Thanks for the feedback @JeremyP :) – Paulo Mattos May 04 '17 at 19:24
  • What about `lazy private var someViews: UILabel = { return self.createSomeViews() }()` or something similar? Having an implicitly unwrapped optional that can be referenced before `viewDidLoad()` (think about `init(with:)`) would be potentially dangerous, isn't it? – Alejandro Iván Aug 20 '18 at 22:54