【问题标题】:How Can I Match Swift 4 KVO on Non-Objective-C Type?如何在非 Objective-C 类型上匹配 Swift 4 KVO?
【发布时间】:2017-12-13 03:40:42
【问题描述】:

我有一个在异步进程中使用的Result 类型:

internal enum Result<T> {

    case success(T)

    case failure(Error)

}

我还有一个APIDataResultContext,用于在Operation 子类之间传递数据:

internal final class APIDataResultContext: NSObject {

    // MARK: Properties

    private let lock = NSLock()

    private var _result: Result<Data>!

    internal var result: Result<Data>! {
        get {
            lock.lock()
            let temp = _result
            lock.unlock()
            return temp
        } 
        set {
            lock.lock()
            _result = newValue
            lock.unlock()
        }
    }

}

在我的单元测试中,我需要确定何时在APIDataResultContext 实例中设置了result。我不能使用 KVO,因为我的 Result&lt;T&gt; 类型不能标记为 dynamic,因为它不能在 Objective-C 中表示。

除了使用闭包属性或Notification 之外,我不知道还有其他方法可以让我监控result 何时更改,我不想这样做。不过,如果有必要,我会使用这两种方法中的一种。

我可以通过哪些其他方式监控result 的更改?

【问题讨论】:

    标签: swift4 key-value-observing


    【解决方案1】:

    我最终向APIDataResultContext 添加了一个闭包属性:

    internal final class APIDataResultContext {
    
        // MARK: Properties
    
        internal var resultChanged: (()->())?
    
        private let lock = NSLock()
    
        private var _result: Result<Data>!
    
        internal var result: Result<Data>! {
            get {
                lock.lock()
                let temp = _result
                lock.unlock()
                return temp
            }
            set {
                lock.lock()
                _result = newValue
                lock.unlock()
                resultChanged?()
            }
        }
    

    }

    我在测试中使用闭包来确定 result 何时更改:

    internal func testNeoWsFeedOperationWithDatesPassesDataToResultContext() {
        let operationExpectation = expectation(description: #function)
        let testData = DataUtility().data(from: "Hello, world!")
        let mockSession = MockURLSession()
        let testContext = APIDataResultContext()
        testContext.resultChanged = {
            operationExpectation.fulfill()
            guard let result = testContext.result else {
                XCTFail("Expected result")
                return
            }
            switch result {
            case .failure(_):
                XCTFail("Expected data")
            case .success(let data):
                XCTAssertEqual(data, testData, "Expected '\(testData)'")
            }
        }
        NeoWsFeedOperation(context: testContext, sessionType: mockSession, apiKey: testAPIKey, startDate: testDate, endDate: testDate).start()
        mockSession.completionHandler?(testData, nil, nil)
        wait(for: [operationExpectation], timeout: 2)
    }
    

    【讨论】:

      【解决方案2】:

      你已经解决了这个问题(你所做的可能就是我要做的),但为标题问题提供字面答案可能仍然有价值:can you use非 Objective-C 类型上的 KVO?

      事实证明,这并不难做到,尽管它有些难看。基本上,您需要创建一个类型为Any 的Objective-C 属性,其Objective-C 名称与真实属性的Swift 名称相同。然后,将willSetdidSet 处理程序放在为Objective-C 属性调用适当KVO 方法的真实属性上。所以,像:

      @objc(result) private var _resultKVO: Any { return self.result }
      internal var result: Result<Data>! {
          willSet { self.willChangeValue(for: \._resultKVO) }
          didSet { self.didChangeValue(for: \._resultKVO) }
      }
      

      (为简单起见,我假设result 是您的存储属性,并从等式中删除锁和私有属性)

      需要注意的是,在构造要观察的关键路径时,您必须使用 _resultKVO 而不是 result,这意味着如果需要从对象外部观察到,则不能使用 _resultKVO @ 987654329@,你必须用它弄乱你的班级界面。但事情就是这样。

      同样,我可能不会为您的特定用例执行此操作(如果您这样做了,您显然可以在resultset 中触发通知,而不必为willSet 和@987654333 烦恼@),但在某些情况下,这可能很有用,最好有一个描述如何做的答案作为参考。

      【讨论】:

      • 我希望这能奏效(我真正希望的是基于 Swift 的原生 KVO,但人们可以梦想......)但我无法从 Swift 方面让它工作。当您尝试使用 Swift 4 键路径的东西观察它时,它会在尝试将 _resultKVO 转换为 Result&lt;Data&gt; 时崩溃(在我的情况下,它是通过泛型转换为 String)。
      • @DeanKelly 我已经做过很多次这种事情了。如果您可以创建一个问题来显示您的具体问题以及您遇到的崩溃,并在附加到该问题的评论中标记我,我可以找出您的情况出了什么问题。
      • 我现在明白了。试图观察泛型中提供类型所需的非私有变量。我可以重新配置周围的 API 以处理 Observable 并改为执行此操作。不是最漂亮的东西,但它确实有效。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-15
      • 1970-01-01
      • 1970-01-01
      • 2018-01-21
      相关资源
      最近更新 更多