简体   繁体   中英

What is the point of using implicitly unwrapped optional in this example?

This is from the docs, section Failable Initializers for Classes :

class Product {

     let name: String!
     init?(name: String) {
           self.name = name
           if name.isEmpty { return nil }
     }
}


if let bowTie = Product(name: "") {
   // no need to check if bowTie.name == nil
   print("The product's name is \(bowTie.name)")
}

This is described like:

In the example above, the name property of the Product class is defined as having an implicitly unwrapped optional string type (String!). Because it is of an optional type, this means that the name property has a default value of nil before it is assigned a specific value during initialization. This default value of nil in turn means that all of the properties introduced by the Product class have a valid initial value. As a result, the failable initializer for Product can trigger an initialization failure at the start of the initializer if it is passed an empty string, before assigning a specific value to the name property within the initializer.

Take a look at the last sentence:

As a result, the failable initializer for Product can trigger an initialization failure at the start of the initializer if it is passed an empty string, before assigning a specific value to the name property within the initializer.

This can't be seen from the provided code . In the provided code, it can be seen that assignment has occurred before return nil part, and either String (non-optional) or String? (optional) would work.

Another thing is that in provided example, it make no sense to use an implicitly unwrapped optional if it is defined as constant. Constants have to be initialized to default values right before the init is finished.

Any thoughts on this, or does anybody see the reason not to file a radar about this? Maybe I am missing something ? I have an idea actually why is implicitly unwrapped optional used in here, but it is a bad example. For me, this would make more sense:

class Product {

    var name: String! //let is changed to var

    init?(name: String) {

       if name.isEmpty { return nil } //Check here if passed value is non-empty
       self.name = name
    }
}

This way, a initialization failure can be triggered before the any assignment to the name property.

All your concerns about misleading documentation are right.

Also, please note that in Swift 2.2 returning early from a failable initializer does work , even before all properties are initialized:

class Product {
    let name: String
    init?(name: String) {
        if name.isEmpty { return nil }
        self.name = name
    }
}

Another change is that deinit is not called any more when returning nil from the failable initializer.

From Xcode 7.3 beta 2 Release Notes :

Inside a class, a designated initializer that is either failable ( init?() ) or throwing ( init() throws ) is allowed to exit before initializing all stored properties and calling super.init() . This behavior is supported, making designated initializers more consistent with convenience initializers. Convenience initializers can also fail before performing a self.init() delegation.

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