Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swift: why I can't call method from override init?

Tags:

swift

I have following code example (from PlayGround):

class Serializable : NSObject{
override init() { }
}

class Device : Serializable{

    var uuid:String

    override init() {

        println("init ")

        self.uuid = "XXX"

       self.uuid = Device.createUUID()

        println(self.uuid)

    }

   class func createUUID() -> String{
       return "XXX2"
    }
}


var device = Device()

You can notice that I implemented createUUID method as static.

But why I can't call this method from init not in static way? :

class Serializable : NSObject{
override init() { }
}

class Device : Serializable{

    var uuid:String

    override init() {

        // tried
        // super.init()

        println("init ")

        self.uuid = "XXX"

       self.uuid = self.createUUID() //  ERROR
       self.uuid = createUUID() // ERROR

        println(self.uuid)

        // tried
        // super.init()

    }

  func createUUID() -> String{
       return "XXX2"
    }
}


var device = Device()

Without inheritance it works properly:

class Device {

    var uuid:String

     init() {

        println("init ")

        self.uuid = "XXX"

       self.uuid = self.createUUID()

        println(self.uuid)

    }

    func createUUID() -> String{
       return "XXX2"
    }
}


var device = Device()
like image 710
snaggs Avatar asked Feb 10 '15 11:02

snaggs


People also ask

How do I override init in Swift?

Override initializer In Swift initializers are not inherited for subclasses by default. If you want to provide the same initializer for a subclass that the parent class already has, you have to use the override keyword.

Is Init method override?

Overriding init()Override the class initializer init() to initialize or allocate resources for the servlet instance's life, such as a counter. The init() method runs after the servlet is instantiated but before it accepts any requests.

Do you need to call super init in Swift?

This is called class inheritance or subclassing, the class you inherit from is called the “parent” or “super” class, and the new class is called the “child” class. For safety reasons, Swift always makes you call super. init() from child classes – just in case the parent class does some important work when it's created.


Video Answer


3 Answers

There are two competing initialization safety checks that are causing your problem.

Safety check 1

A designated initializer must ensure that all of the properties introduced by its class are initialized before it delegates up to a superclass initializer.

and

Safety check 4

An initializer cannot call any instance methods, read the values of any instance properties, or refer to self as a value until after the first phase of initialization is complete.

Here is how it works.

override init() {
    super.init() // Fails safety check 1: uuid is not initialized.
    uuid = createUUID()
}

conversely,

override init() {
    uuid = createUUID() // Fails safety check 4: cannot call an instance method before initialization is complete.
    super.init()
}

Thanks to @Ruben in his answer

like image 188
Jeffery Thomas Avatar answered Nov 15 '22 11:11

Jeffery Thomas


This is exactly why Implicitly Unwrapped Optionals exists.

“Implicitly unwrapped optionals are useful when an optional’s value is confirmed to exist immediately after the optional is first defined and can definitely be assumed to exist at every point thereafter. The primary use of implicitly unwrapped optionals in Swift is during class initialization(...)”

Excerpt From: Apple Inc. “The Swift Programming Language (Swift 3).” iBooks. https://itun.es/gb/jEUH0.l

All you need to do is implicityly unwrapp uuid:

class Device: Serializable {

    var uuid: String!

    override init() {
        super.init()
        self.uuid = createUUID()
    }

    func createUUID() -> String {
        return "XXX2"
    }
}
like image 34
Greg Leszek Avatar answered Nov 15 '22 09:11

Greg Leszek


Initialization in Swift with inheritance is kind of tricky. You must have all your child object's instance variables instantiated before you can call super.init(), and yet you need to call super.init() before you can call methods on the self object.

My suggestion is to give uuid a temporary value before you call super.init(), and then generate the uuid with your instance method afterwards:

class Device : Serializable{

    var uuid:String

    override init() {
        // First you need to initialize this classes instance variables
        // This feels a little silly to put in a temporary value, but if you don't want to use an optional, then it is necessary
        self.uuid = ""

        // Next you need to initialize super before you can use the `self` object
        super.init()

        // Since you are calling `createUUID()`, you are calling an instance method on the `self` object
        // Your object must call `super.init()` before you can do so
        self.uuid = self.createUUID()
    }

    func createUUID() -> String{
        return "XXX2"
    }
}

Here is the reference for initialization in Swift on classes that inherit from other classes. It is pretty long, but you can summarize it as:

override init() {
    // Make sure all instance variables for the child class are instantiated
    super.init()
    // Do other initialization that requires the `self` object here
}

But you can also read about convenience initializers there too, which are pretty handy.

like image 36
cjwirth Avatar answered Nov 15 '22 09:11

cjwirth