Swift协议:由于递归循环而导致自定义属性设置器崩溃

时间:2016-03-28 17:20:47

标签: ios swift swift2

Swift:由于无限循环而导致自定义属性设置器崩溃

public protocol RequestType {
    var parameters: AnyObject? { get set }
}

public extension RequestType {

    public var parameters: AnyObject? {
        get { return nil }
        set { parameters = newValue }
    }

    public printParameters() {
        print(parameters)
    }

}

用法:

class HTTPBinRequest: RequestType { }

let request = HTTPBinRequest()
request.parameters = ["name" : "Rahul"]
request.printParameters()

它崩溃了,因为它卡在递归循环中。

但是当我在类中声明属性并将setter留空时,它才起作用。

class HTTPBinRequest: RequestType { 
    public var parameters: AnyObject?
}

public extension RequestType {

    public var parameters: AnyObject? {
        get { return nil }
        set {}
    }

    public printParameters() {
        print(parameters)
    }

}

let request = HTTPBinRequest()
request.parameters = ["name" : "Rahul"]

有人可以建议我如何更好地解决这个问题。

1 个答案:

答案 0 :(得分:2)

您似乎正在尝试在协议扩展中创建存储属性。你不能这样做。您只能在协议扩展中创建计算属性

这是一个存储的属性:

class A {
    var value: Int?      // Stored property
}

这是一个计算属性:

class B {                       
    var someString: String?     // Stored property

    var value: Int? {           // Computed property
        get { return Int(someString) }
        set { someString = "\(newValue)" }
    }
}

计算属性不能自行设置 - 如果您尝试这样做,它会创建您正在经历的递归循环。但它可以执行其他操作,例如设置类的其他存储属性,如上例中接受Int作为新值但随后将其存储为String的示例。永远不会存储Int值本身。当您检索Int值时,每次访问时都会从someString计算它。

如果您尝试将此作为协议执行,则可以将计算属性放在协议中,而不是存储的属性:

protocol BProtocol {
    var someString: String? { get set }
    var value: Int? { get set }
}

extenstion BProtocol {

    var value: Int? {        
        get { return Int(someString) }
        set { someString = "\(newValue)" }
    }
}

但是你仍然需要在类中定义存储的属性:

class B: BProtocol {                       
    var someString: String?
}

但您不需要定义value属性。 class B将从BProtocol扩展名继承该内容。

因此,对您的问题的简短回答是,在您的类中创建存储的属性,而不是在协议扩展中。

编辑:

根据评论,您可能需要使用双层协议。第一个协议规定了不使用parameters属性的对象的要求:

protocol BaseProtocol {
   var someStuff: String? { get set }
}

protocol BaseProtocolWithParameters: BaseProtocol {
   var parameters: AnyObject? { get set }
}

我之所以这么说是因为通常设计要求用户需要创建从未使用过的属性的设计是不好的。如果某些对象不能使用parameters,则不应要求这些对象符合他们不需要的协议要求。他们可以符合BaseProtocol。实际需要使用parameters的对象可以符合继承自BaseProtocolWithParameters的{​​{1}}。