2

I want to subclass CAShapeLayer so it will hold a reference to the corresponding object in the data model:

class CPCoursePointLayer : CAShapeLayer
{
    let itsDataRef :CPCoursePoint
    let itsEventData :CPEventData

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }

    init( coursePoint: CPCoursePoint, in eventData :CPEventData)
    {
        itsDataRef = coursePoint
        itsEventData = eventData
    }
}

Where itsDataRefand itsEventData are references (pointers if you like) to the underlying data. This way I can update the model if I have moved the an instance of may layer class, or draw it using graphic styles specified in the eventData. My CPCoursePointLayerdoes not own these variables, to use a Rust term.

Now that I'm required to implement a init(coder:) initializer, how do I initialize my instance variables from nothing? Can I decode a 'pointer' from the NSCoder? That doesn't make sense to me...

Although I'm never going to call init(coder:), myself, I guess it's there because the system might call it. Then I will have an instance that is unusable...

I want to avoid the extra burden of doing the extra if let in every method if I declare my instance variables as optional (?).

1
  • 1
    You can implement the method and just have it throw a fatal error since you won't be using it. Commented Jan 14, 2019 at 21:43

2 Answers 2

2

You essentially have three options:

  1. Declare the variables as implicitly unwrapped optionals
  2. Have init?(coder:) fail, either fatally or not
  3. Have CPCoursePoint and CPEventData implement unarchiving via init?(coder:)

Option 1:

class CPCoursePointLayer : CAShapeLayer
{
    let itsDataRef :CPCoursePoint!
    let itsEventData :CPEventData!

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }

    init( coursePoint: CPCoursePoint, in eventData :CPEventData)
    {
        itsDataRef = coursePoint
        itsEventData = eventData
    }
}

This will result in a crash if you reference itsDataRef or itsEventData and your object wasn't initialised by your new initialiser.

Option 2(a)

class CPCoursePointLayer : CAShapeLayer
{
    let itsDataRef :CPCoursePoint
    let itsEventData :CPEventData

    required init?(coder aDecoder: NSCoder) {
        return nil
    }

    init( coursePoint: CPCoursePoint, in eventData :CPEventData)
    {
        itsDataRef = coursePoint
        itsEventData = eventData
    }
}

This will simply fail to initialise the object if init(coder:) is used.

Option 2(b)

class CPCoursePointLayer : CAShapeLayer
{
    let itsDataRef :CPCoursePoint
    let itsEventData :CPEventData

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) is not supported for this class"
    }

    init( coursePoint: CPCoursePoint, in eventData :CPEventData)
    {
        itsDataRef = coursePoint
        itsEventData = eventData
    }
}

This will cause your program to crash if init(coder:) is called.

Option 3

class CPCoursePointLayer : CAShapeLayer
{
    let itsDataRef :CPCoursePoint
    let itsEventData :CPEventData

    required init?(coder aDecoder: NSCoder) {
        guard let itsDataRef = aDecoder.decodeObject(forKey:"itsDataRef"),
              let itsEventData = aDecoder.decodeObject(forKey:"itsEventData") else {
            return nil
        }
        self.itsDataRef = itsDataRef
        self.itsEventData = itsEventData
        super.init(coder: aDecoder)
    }

    init( coursePoint: CPCoursePoint, in eventData :CPEventData)
    {
        itsDataRef = coursePoint
        itsEventData = eventData
    }
}

Option 2(b) is probably the most common as it is a "fail fast" - You should catch this during testing pretty quickly if that initialiser is called.

Sign up to request clarification or add additional context in comments.

Comments

1

The typical solution, if you don't want to support NSCoding in your subclass, is to fatalError in init(coder:). In fact, the compiler will even offer that as a fix-it if you omit the initializer entirely:

fix-it

So just do that:

class CPCoursePointLayer: CAShapeLayer {
    let itsDataRef: CPCoursePoint
    let itsEventData: CPEventData

    init(coursePoint: CPCoursePoint, in eventData: CPEventData) {
        itsDataRef = coursePoint
        itsEventData = eventData
        super.init()
    }

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

}

The system does not normally try to decode an instance of your custom layer.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.