Swift 2:在尝试读取时意外写入 属性

Swift 2: Unexpectedly writing to a property when trying to read it

我遇到的问题是我的 属性 的 willSetdidSet 被调用,即使我 只读 属性,这会破坏我的应用程序。

我把我的问题浓缩成了一个游乐场。取消注释 #1 以查看问题,或取消注释 #2 以查看预期的行为。

这是怎么回事?

protocol Departure
{
    var line: String? { get }
}

class MyDeparture : Departure
{
    var line: String? = "SomeString"
}

#if true
    // #1: this causes a write to tableContet later (!)
    typealias TableSection = (title: String, rows: [Departure])
    #else
    // #2: this doesn't cause a write to tableContent later
    typealias TableSection = (title: String, rows: [MyDeparture])
#endif

var tableContent: [TableSection] = [ TableSection(title: "MySectionTitle", rows: [ MyDeparture() ]) ]
{
willSet { print("willSet tableContent") }
didSet { print("didSet tableContent") }
}

func getDepartureDescription() -> String? {
    print("start getDepartureDescription")
    defer { print("end getDepartureDescription") }

    #if true
        // writes to tableContent in case #1
        let lineNumber = tableContent[0].rows[0].line
        #else
        // never writes to table content
        let row = tableContent[0].rows[0]
        let lineNumber = row.line
    #endif

    return "Your line is \(lineNumber)"
}

getDepartureDescription()

这会打印

start getDepartureDescription
willSet tableContent
didSet tableContent
end getDepartureDescription

我正在使用 Xcode 7 (7A218) 转基因种子。在 Xcode 6.4 和 Swift 1.2.

中一切都按预期工作
边注:

起初我认为运行时是——在阅读TableSection.rows时——从分配给它的[MyDeparture]数组创建一个新的[Departure]数组。但即使以我能想到的最明确的方式更正它也没有解决问题:

// More correct types makes no difference:
var departures: [Departure] {
    var result = Array<Departure>()
    result.append(MyDeparture())
    return result
}

var tableContent: [TableSection] = [ TableSection(title: "MyTitle", rows:  departures ) ]

正在进行某种惰性初始化。只有当它到达第

行时
let lineNumber = tableContent[0].rows[0].line

那个运行时间好像是在填充数组的内容。

如果您将数组声明为包含符合 Departure 协议的元素,运行时间不知道 tableContent 的元素实际有多大,因为 classes和结构可以符合Departure。因此,我认为它正在重新创建数组并错误地触发 didSetwillSet

我试过将您的协议限制为 classes,如下所示:

protocol Departure: class
{
    var line: String? { get }
}

问题消失了,因为现在 运行时间知道数组只能包含 class 个引用。

我认为这是一个错误,您应该向 Apple 提出。