KV在Swift 4中保留一个协议

pro*_*oxi 11 key-value-observing swift-protocols swift4

我正在努力在Swift 4中使用新的强类型KVO语法来观察仅通过协议可见的属性:

import Cocoa

@objc protocol Observable: class {
    var bar: Int { get }
}

@objc class Foo: NSObject, Observable {
    @objc dynamic var bar = 42
}

let implementation = Foo()

let observable: Observable = implementation

let observation = observable.observe(\.bar, options: .new) { _, change in
    guard let newValue = change.newValue else { return }

    print(newValue)
}

implementation.bar = 50
Run Code Online (Sandbox Code Playgroud)
error: value of type 'Observable' has no member 'observe'
let observation = observable.observe(\.bar, options: .new) { _, change in
Run Code Online (Sandbox Code Playgroud)

显然,Observable不是NSObject.但我不能简单地将它NSObject强制转换,因为keypath的类型与对象的类型不匹配.

我试着更明确地说明这种类型:

let observable: NSObject & Observable = implementation
Run Code Online (Sandbox Code Playgroud)

但:

error: member 'observe' cannot be used on value of protocol type 'NSObject & Observable'; use a generic constraint instead
let observation = observable.observe(\.bar, options: .new) { _, change in
Run Code Online (Sandbox Code Playgroud)

我想做的不可能吗?这似乎是一个常见的用例.使用旧的#keypath语法很容易完成.你能提供任何其他选择吗?谢谢.

Ole*_*ann 8

此代码在Swift 4.1.2(Xcode 9.4)中编译和运行:

import Foundation

@objc protocol Observable: AnyObject {
    var bar: Int { get }
}

@objc class Foo: NSObject, Observable {
    @objc dynamic var bar = 42
}

let implementation = Foo()

let observable: NSObject & Observable = implementation

func observeWrapper<T: NSObject & Observable>(_ object: T) -> NSKeyValueObservation {
    return object.observe(\.bar, options: .new) { _, change in
        guard let newValue = change.newValue else { return }
        print(newValue)
    }
}

let observation = observeWrapper(observable)
implementation.bar = 50
withExtendedLifetime(observation, {})
Run Code Online (Sandbox Code Playgroud)

我所做的只是将调用包装observe在一个受限制的泛型函数中NSObject & Observable.

尽管引入了泛型,但仍然可以将协议类型传递observable给这个新函数.说实话,我无法解释为什么会这样.

编辑:这可以解释它:Swift中的协议通常不符合自己,因此即使约束匹配,也不允许使用存在(协议类型)调用泛型函数.但是@objc协议(没有静态要求)有一个例外,它们符合自己的要求.我想这是有效的,因为Observable有标记@objc.

  • 如果您需要通过`@objc` 来标记类中的每个属性,那么在类本身上使用`@objcMembers` 修饰符会更优雅。 (2认同)