【问题标题】:How to define initializers in a protocol extension?如何在协议扩展中定义初始化器?
【发布时间】:2015-09-12 14:40:42
【问题描述】:
protocol Car {
     var wheels : Int { get set}

     init(wheels: Int)

}

extension Car {
    init(wheels: Int) {
        self.wheels = wheels
    }
}

在 self.wheels = 轮子上我得到错误

Error: variable 'self' passed by reference before being initialized

如何在协议扩展中定义初始化器?

【问题讨论】:

  • 如果你使用简单的结构,你可以省略协议中的初始化器,因为它会被编译器合成!

标签: swift initialization swift2 protocols swift-extensions


【解决方案1】:

正如您所见,这在这些情况下不起作用,因为在编译时,必须确保在使用 struct/enum/class 之前初始化所有属性。

您可以要求另一个初始化程序,以便编译器知道所有属性都已初始化:

protocol Car {
    var wheels : Int { get set }
    // make another initializer
    // (which you probably don't want to provide a default implementation)
    // a protocol requirement. Care about recursive initializer calls :)
    init()
    init(wheels: Int)

}

extension Car {
    // now you can provide a default implementation
    init(wheels: Int) {
        self.init()
        self.wheels = wheels
    }
}

// example usage

// mark as final
final class HoverCar: Car {
    var wheels = 0
    init() {}
}

let drivableHoverCar = HoverCar(wheels: 4)
drivableHoverCar.wheels // 4

从 Xcode 7.3 beta 1 开始,它可以按预期与structs 一起使用,但不能与类一起使用,因为如果它们不是final,则协议中的init(wheels: Int)required init,它可以被覆盖,因此它不能被覆盖通过扩展添加。解决方法(正如编译器所建议的那样):将 class final 设为。

另一种解决方法(深入;没有final class

要使用类而不使它们成为最终的,您还可以删除协议中的init(wheels: Int) 要求。看起来它的行为与以前没有什么不同,但请考虑以下代码:

protocol Car {
    var wheels : Int { get set }
    init()
    // there is no   init(wheels: Int)
}

extension Car {
    init(wheels: Int) {
        self.init()
        print("Extension")
        self.wheels = wheels
    }
}

class HoverCar: Car {
    var wheels = 0
    required init() {}
    init(wheels: Int) {
        print("HoverCar")
        self.wheels = wheels
    }
}

// prints "HoverCar"
let drivableHoverCar = HoverCar(wheels: 4)

func makeNewCarFromCar<T: Car>(car: T) -> T {
    return T(wheels: car.wheels)
}

// prints "Extension"
makeNewCarFromCar(drivableHoverCar)

因此,如果您从通用上下文中创建Car,其中调用init 的类型仅称为Car,即使在HoverCar 中定义了初始化程序,也会调用扩展初始化程序。这只是因为协议中没有init(wheels: Int) 要求。

如果您添加它,您会遇到将class 声明为final 的前一个问题,但现在它会打印两次“HoverCar”。无论哪种方式,第二个问题可能永远不会发生,因此它可能是一个更好的解决方案。

旁注:如果我犯了一些错误(代码、语言、语法……),欢迎您纠正我:)

【讨论】:

  • 你能添加类的声明来进一步说明你的解决方案吗?
  • @CraigGrummitt 你是指哪个类? Car 是一个协议,它的声明是完整的。
  • 感谢您展示课程。我在这个解决方案中看到的主要问题是,您要么需要为 Wheels 声明一个默认值(在 var 声明或所需的 init() 中),要么需要将其声明为隐式展开的 Optional。这是解决问题的好方法,我只是想知道这本身是否听起来像是我们不应该在协议扩展中实现初始化程序! :)
  • @CraigGrummitt 我完全同意你的看法。强制您为轮子设置默认值的默认初始化要求有点糟糕。然而,关于 Swift 3 的部分初始化器可以解决这个问题的讨论正在进行中。
  • @Qbyte drivableHoverCar.wheels 打印 4,而不是 6。
【解决方案2】:

我的理解是这是不可能的,因为协议扩展无法知道符合的类或结构具有哪些属性 - 因此不能保证它们被正确初始化。

如果有办法解决这个问题,我很想知道! :)

【讨论】:

    【解决方案3】:

    @Qbyte 是正确的。

    另外你可以看看我的Configurable

    因为我有Initable 协议

    public protocol Initable {
        // To make init in protocol extension work
        init()
    }
    
    public extension Initable {
        public init(@noescape block: Self -> Void) {
            self.init()
            block(self)
        }
    }
    

    那么为了符合它

    extension Robot: Initable { }
    

    我有两种方法,使用final 或实现init

    final class Robot {
        var name: String?
        var cute = false
    }
    
    class Robot {
        var name: String?
        var cute = false
    
        required init() {
    
        }
    }
    

    【讨论】:

    • 不错。最后是显示重要部分的“我的”链接
    【解决方案4】:

    可能不一样,但在我的例子中,我没有使用 init,而是使用静态函数来返回类的对象。

    protocol Serializable {
       static func object(fromJSON json:JSON) -> AnyObject?
    }
    
    class User {
        let name:String
    
        init(name:String) {
            self.name = name
        }
    }
    
    extension User:Serializable {
    
        static func object(fromJSON json:JSON) -> AnyObject? {
            guard let name = json["name"] else {
                return nil
            }
    
            return User(name:name)
         }
    
    }
    

    然后要创建对象,我会执行以下操作:

    let user = User.object(fromJSON:json) as? User
    

    我知道这不是有史以来最好的东西,但它是我能找到的最好的解决方案,不将业务模型与数据层耦合。

    注意:我很懒,我直接在评论中编码了所有内容,所以如果有什么不起作用,请告诉我。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多