Search code examples
swiftinitsuperself

Implicit Unwrapping and Initializers


I have a subclass of UIView, which instantiates several sublayers. For convenience, I want a direct reference to one in particular, and because it has the same lifetime as the view, it seems reasonable to make it a let constant. The problem is that I want the sublayer to have a similarly immutable reference back to its owner/parent view, so I try to pass this to the layer’s initializer:

class MyView: UIView{
    let myLayer: MyLayer!

    required init?(coder aDecoder: NSCoder){ 
        super.init(coder: aDecoder)
        self.myLayer = MyLayer(owner: self)   //  <- The problem line.
        self.layer.addSublayer(self.myLayer)
    }
}

class MyLayer: CAShapeLayer {
    unowned let owner: MyView

    init(owner: MyView) {
        self.owner = owner
        super.init ()   
    }
 }

If I put the call to the layer initializer before the call to MyView’s super.init, I get the error “'self' used before 'super.init' call.” I take this to mean it’s permissible (preferred I always thought?) to use self in a property initialization, but it doesn’t yet have a value that can be passed as a parameter?

But if I place the line after super.init as shown, I get two errors: “Property 'self.myLayer' not initialized at super.init call” and “Immutable value 'self.myLayer' may only be initialized once.” This puzzles me, as I thought one point of an implicitly unwrapped optional was that it had a valid nil value from its declaration, and that this did not count as the one and only assignment a let statement permits. (It also sounds a trifle contradictory: something wasn’t initialized, but after an attempted initialization, it wound up initialized twice, nevertheless?)

I know I can get around the problem by dropping my obsession with immutables, but is there a way to do this properly? I’m all in favor of Swift’s safety checking, but is there an actual danger here? I’d think the implicit unwrap would signal the compiler that the programmer is aware of a possible issue and is looking out for consequences.


Solution

  • I thought one point of an implicitly unwrapped optional was that it had a valid nil value from its declaration, and that this did not count as the one and only assignment a let statement permits.

    Unfortunately, it counts. This behavior is fixed into current style after this update:

    Xcode Release Notes > Xcode 6.3 > Swift Language Changes

    You may have found some workarounds, but I think this would be very near to what you want:

    class MyView: UIView          {
        private(set) var myLayer: MyLayer!
    
        required init?(coder aDecoder: NSCoder)   {
            super.init(coder: aDecoder)
            self.myLayer = MyLayer(owner: self)
            self.layer.addSublayer(self.myLayer)  }
    }