IOS-SWIFT- 如何解决Apple文档代码中关于KVO的警告?为什么 Xcode 自动更正引入了错误?

IOS-SWIFT- How to resolve the warning from the Apple Documentation Code about KVO? Why Xcode auto correction introduced an error?

为了学习KVO,我复制了https://developer.apple.com/documentation/swift/cocoa_design_patterns/using_key_value_observing_in_swift的代码。如下

 class MyObjectToObserve: NSObject {
        @objc dynamic var myDate = NSDate(timeIntervalSince1970: 0) // 1970
        func updateDate() {
            myDate = myDate.addingTimeInterval(Double(2 << 30)) // Adds about 68 years.
        }
    }

    class MyObserver: NSObject {
        @objc var objectToObserve: MyObjectToObserve
        var observation: NSKeyValueObservation?

        init(object: MyObjectToObserve) {
            objectToObserve = object
            super.init()

            observation = observe(
                \.objectToObserve.myDate,
                options: [.old, .new]
            ) { object, change in
                print("myDate changed from: \(change.oldValue!), updated to: \(change.newValue!)")
            }
        }
    }

    let observed = MyObjectToObserve()
    let observer = MyObserver(object: observed)

    observed.updateDate()

倒数第二行将导致警告,因为未使用 observer。在我按照 Xcode 的建议将 let observer 替换为 _ 之后,警告消失了,但它会给出运行时错误:

线程 1:EXC_BAD_ACCESS(代码=EXC_I386_GPFLT)

我的目标是获得以下问题的答案:

1.Does 谁知道为什么会出错?

2.How 解决警告?

3.Is Swift 中的 KVO 这个例子是最新的吗?语法级别?

Apple 文档中的代码片段仅作为示例 - 这不是完整的实现。

据推测,您将使用 observer 执行其他操作,此时您将不再收到 "not used" 警告。

示例用法(仅用于演示,不被视为生产代码):

class ExampleViewController: UIViewController {

    var observed: MyObjectToObserve!
    var observer: MyObserver!

    override func viewDidLoad() {
        super.viewDidLoad()

        observed = MyObjectToObserve()
        observer = MyObserver(object: observed)

    }

    @IBAction func didTap(_ sender: Any) {
        observed.updateDate()
    }

}

创建一个新的视图控制器;将其 class 设置为 ExampleViewController;添加一个按钮并将其连接到 @IBAction func didTap.

运行 应用程序.. 每次点击按钮时,您都会在调试控制台中看到 print() 输出。

具有讽刺意味的是,Apple 的示例比它需要的更复杂。也就是说,甚至不需要 MyObserver 对象。您可以简单地在您实际观察的对象上调用 observe

这看起来像下面这样:

class MyObjectToObserve: NSObject {
    @objc dynamic var myDate = NSDate(timeIntervalSince1970: 0) // 1970
    func updateDate() {
        myDate = myDate.addingTimeInterval(Double(2 << 30)) // Adds about 68 years.
    }
}

let observed = MyObjectToObserve()
let observation = observed.observe(\MyObjectToObserve.myDate, options: [.old, .new]) { object, change in
    print("myDate changed from: \(change.oldValue!), updated to: \(change.newValue!)")
}

observed.updateDate()

不幸的是,这个更简单的版本仍然存在原来的问题。您需要 let observation = ... 将其保持在 KVO 工作的范围内,然后您会收到警告。

所以要抑制警告,您需要对结果做一些事情。添加以下行:

_ = observation.hash    // Suppress warning on observation

希望编译器能优化它(如果不是,它只是分配一个整数,所以开销可以忽略不计),但它确实可以抑制警告。我包含注释,以便下一个查看代码的程序员不会删除看似无用的行。

注意:我很少再使用 KVO,因为我发现 Combine 更强大,也更少麻烦。然而,有时它仍然是必要的。即使在大多数情况下,我的代码看起来更像@DonMag 的代码,其中对必要对象的引用作为控制器的成员保存。我确实使用这种抑制“hack”的地方是在我的一些单元测试中,我在其中手动设置 KVO 并且不希望我的代码显示警告。