Bilue / swift-style-guide

Style guide & coding conventions for Swift projects
Creative Commons Zero v1.0 Universal
14 stars 3 forks source link

Using lazy closures for thread safe property composition #9

Open dannolan opened 8 years ago

dannolan commented 8 years ago

I've been thrashing through this idiom recently so it's not explicitly a 'pure swift' thing but more of a good way to encapsulate logic within a particular component.

Say you have a text field and you want to perform a stack of modifications to it, you have two ways to do this

func myCoolFunc() {
    let textField = TextField(rect: CGRectZero)
    label.attributedPlaceholder = ...
    textField.attributedText = 
    textField.delegate = self (or some other class)
}

The problem being that what if myCoolFunc is called more than once, this is why you can use lazily evaluated closures on properties to guarantee they'll only be initialized once (and also they give you a convenient way to store item centric logic

lazy var myTextField : UITextField = {
    let textField = TextField(rect: CGRectZero)
    label.attributedPlaceholder = ...
    textField.attributedText = 
    textField.delegate = self (or some other class)
    return textField
}()

IIRC lazy attributes/properties are de facto thread-safe and lazy closures are only executed ONCE meaning you get a decoupling of your property elements from your actual imperative code (if you're exclusively laying out in code). Something I've been mulling over but something I think is a good addition to the guide. Happy to discuss with others.

sboddeus commented 8 years ago

Under 'Lazy Stored Properties' in Apple's 'The Swift Programming Book' there is a note that states:

"If a property marked with the lazy modifier is accessed by multiple threads simultaneously and the property has not yet been initialised, there is no guarantee that the property will be initialised only once."

dannolan commented 8 years ago

Ah darn, then my information is massively incorrect :(

sboddeus commented 8 years ago

Yeah, I guess there is always good ol' dispatch_once. Sucks that isn't the default behaviour.

dannolan commented 8 years ago

Actually stuff it, I'll ping Joe from the swift team and see if this is a temporary issue or something they won't fix. 

— Sent from my iPhone

On Tue, Dec 1, 2015 at 9:09 PM, sboddeus notifications@github.com wrote:

Yeah, I guess there is always good ol' dispatch_once. Sucks that isn't the default.

Reply to this email directly or view it on GitHub: https://github.com/Bilue/swift-style-guide/issues/9#issuecomment-160922975

edwardaux commented 8 years ago

I like this general idea, but the only way I've been able to do it safely is something like:

class AwesomeClass {
    var myField: UITextField = AwesomeClass.setupCoolField()

    static private func setupCoolField() -> UITextField {
        return UITextField()
    }
}

Double edged sword here... on one hand, it has moved the setup logic away from the definition of myField; on the other, it offers a re-usable method that can be used to initialise multiple variables. YMMV.

sboddeus commented 8 years ago

In that case what do you mean by "Safe"? Because that is also not 'thread-safe'.

edwardaux commented 8 years ago

Why not thread-safe? The call to initialise myField is injected into the init() method by the compiler. If init() method hasn't returned yet, how would another thread be able to get a reference to the object under inilitialisation? Or have I missed something?

sboddeus commented 8 years ago

Oops, you are completely correct. I obviously haven't had enough coffee this morning. For some reason I was still thinking about lazy vars.