Protocol Extension Initializer

后端 未结 4 1120
谎友^
谎友^ 2020-12-10 04:34

I would like to know what the protocol equivalent is for an initializer in a simple class that only contains initializing functionality and is only intended to be extended i

相关标签:
4条回答
  • 2020-12-10 05:07
    protocol Thing {
        var color: UIColor {get set}
    }
    

    Awesome, no problems.

    extension Thing {
        init(color: UIColor) {
            self.color = color
        }
    }
    

    No. That's never going to work. This breaks way too many rules. The first and most important is that this doesn't necessarily set all the properties. Consider your NamedThing. What is name in this case? What happens if the color setter fetches other properties that haven't been set yet? The compiler can't see every possible implementation of this yet, so it has no idea if color is just an ivar or something wildly more complicated. No, this isn't going to work.

    The real problem is "an abstract class that can be extended in a concrete class." Forget classes. Forget inheritance. Swift is all about composition and protocols, not inheritance.

    So let's think about the example you describe in the comments (though in Cocoa, there are no "abstract classes," either). Let's assume that setting color is in fact a lot of code that you don't want to duplicate. That's no problem. You just need a function.

    import UIKit
    
    protocol Thing {
        var color: UIColor {get set}
    }
    
    private extension Thing {
        static func colorForColor(color: UIColor) -> UIColor {
            // We don't really use the color directly. We have some complicated code that we don't want to repeat
            return color
        }
    }
    
    final class NamedThing: Thing {
        var name: String
        var color: UIColor
    
        init(name: String, color: UIColor) {
            self.name = name
            self.color = NamedThing.colorForColor(color)
        }
    }
    

    Since the point of your extension is to handle partial initialization, just let it calculate the part you need. Don't try to make it an initializer in an extension, since then it would have to be responsible for initializing everything, and that is very hard to do correctly when you mix it with inheritance.

    0 讨论(0)
  • 2020-12-10 05:11

    Here's what I had in mind for the "delegate class".

    It's a technique I use to add stored variables to a class using protocols.

    class ManagedColors
    {
       var color:UIColor
       // other related variables that need a common initialisation
       // ...
       init(color:UIColor)
       {
          self.color = color
          // common initialisations for the other variables
       }
    }
    
    protocol ManagedColorClass
    {
        var managedColors:ManagedColors { get }
    }
    
    extension ManagedColorClass 
    {    
        // makes properties of the delegate class accessible as if they belonged to the
        // class that uses the protocol
        var color:UIColor { 
                            get { return managedColors.color } 
                            set { managedColors.color = newValue }
                          }    
    }
    
    
    // NamedThing objects will be able to use .color as if it had been
    // declared as a variable of the class
    //
    // if you add more properties to ManagedColors (and the ManagedColorHost protocol)
    // all your classes will inherit the properties as if you had inherited from them through a superclass
    // 
    // This is an indirect way to achive multiple inheritance, or add additional STORED variables with
    // a protocol
    //
    class NamedThing:ManagedColorClass 
    {
        var name:String
        var managedColors:ManagedColors 
    
        init(name:String,color:UIColor) 
        {
            managedColors = ManagedColors(color:color)
            self.name = name
        }
    }
    
    let red = NamedThing(name:"red", color:UIColor.redColor())
    print(" \(red.name)  \(red.color)")
    
    0 讨论(0)
  • 2020-12-10 05:17

    You have to provide a valid chain of init for creating an instance of a class and that limits your options for initializers in protocols.

    Since your protocol can't be certain to cover all members of the class that uses it, any initializer you declare in your protocol will need to delegate initialization of the "unknown" members of the class to another initializer provided by the class itself.

    I adjusted your example to illustrate this using a basic init() as the delegation initializer for the protocol.

    As you can see, this requires that your class implement initial values for all members when init() is called. In this case I did that by providing default values in each member's declaration. And, since there isn't always an actual initial value for some members, I changed them to auto-unwrap optionals.

    And to make thinks more interesting, your class cannot delegate initialization to a protocol supplied initializer unless it does so through a convenience initializer.

    I wonder if all these restrictions are worth the trouble. I suspect you're trying to use a protocol because you need a bunch of common variables to be initialized consistently between classes that implement the protocol. Perhaps using a delegate class would provide a less convoluted solution than protocols (just a thought).

    protocol Thing:AnyObject
    {
        var color:UIColor! { get set }
        init()
    }
    
    extension Thing 
    {    
        init(color:UIColor)
        {  
           self.init()
           self.color = color
        }
    }
    
    class NamedThing:Thing 
    {
        var name:String!   = nil
        var color:UIColor! = nil
    
        required init() {}
    
        convenience init(name:String,color:UIColor) 
        {
            self.init(color:color)
            self.name = name
        }
    }
    
    0 讨论(0)
  • 2020-12-10 05:29

    I have come to the conclusion that the question is sort of unanswerable, as a protocol extension can not dynamically define properties(unless it provides default values for properties, or declares them as implicitly unwrapped). To resolve this in a more protocol oriented fashion, it requires a different approach, which still involves declaring and initializing all variables in the concrete class, something like:

    import UIKit
    protocol Colorable {
        var color: UIColor {get set}
    }
    protocol Nameable {
        var name: String {get set}
    }
    class ColoredNamedThing: Colorable, Nameable {
        var name: String
        var color: UIColor
    
        init(name: String, color: UIColor) {
            self.name = name
            self.color = color
        }
    }
    
    var coloredNamedThing = ColoredNamedThing(name: "Name", color: UIColor.redColor())
    

    Thank you to @alain-t for the answer I'm going to accept as it most closely finds a solution to my question, despite it including implicitly unwrapped properties.

    Thank you to @rob-napier also for your contribution also.

    0 讨论(0)
提交回复
热议问题