简体   繁体   中英

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

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()

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

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.

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"
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM