如何在协议扩展中定义初始值设定项?

bog*_*gen 45 protocols initialization swift swift-extensions swift2

protocol Car {
     var wheels : Int { get set}

     init(wheels: Int)

}

extension Car {
    init(wheels: Int) {
        self.wheels = wheels
    }
}
Run Code Online (Sandbox Code Playgroud)

on self.wheels = wheels我得到了错误

Error: variable 'self' passed by reference before being initialized
Run Code Online (Sandbox Code Playgroud)

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

Qby*_*yte 62

正如您所看到的,这在这些情况下不起作用,因为在编译时,必须确保在使用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
Run Code Online (Sandbox Code Playgroud)

作为Xcode的7.3的β1的它与工作structs如预期但不与类,因为如果它们不是finalinit(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)
Run Code Online (Sandbox Code Playgroud)

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

如果你添加它,你有前面的问题声明classas,final但现在它打印两次"HoverCar".无论哪种方式,第二个问题可能永远不会发生,因此它可能是更好的解决方案

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


Eiv*_*ler 6

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

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


onm*_*133 5

@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)
    }
}
Run Code Online (Sandbox Code Playgroud)

然后为了顺应它

extension Robot: Initable { }
Run Code Online (Sandbox Code Playgroud)

我有2种方法,使用final或实施init

final class Robot {
    var name: String?
    var cute = false
}

class Robot {
    var name: String?
    var cute = false

    required init() {

    }
}
Run Code Online (Sandbox Code Playgroud)