62
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

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

4

4 回答 4

83

如您所见,这在这些情况下不起作用,因为在编译时,必须确保在使用 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按预期使用,但不能与类一起使用,因为如果它们不在协议中是 afinal并且它可以被覆盖,因此它不能通过扩展添加。解决方法(正如编译器建议的那样):将.init(wheels: Int)required initclass 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)要求而发生。

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

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

于 2015-06-25T23:45:45.980 回答
6

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

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

于 2015-06-25T21:17:06.530 回答
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)
    }
}

然后为了符合它

extension Robot: Initable { }

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

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

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

    required init() {

    }
}
于 2015-12-30T04:35:13.333 回答
2

可能不一样,但在我的情况下,我没有使用 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

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

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

于 2016-05-20T08:51:15.357 回答