我有一个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()
}
}
}
在我的单元测试中,我需要确定何时在result
实例中设置了APIDataResultContext
。我不能使用KVO,因为我的Result<T>
类型不能标记为dynamic
,因为它无法在Objective-C中表示。
我不知道另一种方式可以让我监视result
何时更改,而不是使用闭包属性或Notification
,我不想这样做。不过,如果有必要的话,我会选择其中一个。
我可以通过哪些其他方式监控result
的变化?
我最终向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)
}
你已经解决了这个问题(你所做的可能就是我所做的),但是为标题问题提供一个字面答案可能仍然有价值:如何在非Objective-C类型上使用KVO?
事实证明,这并不难,尽管有点难看。基本上,您需要创建一个类型为Any
的Objective-C属性,其具有与Real属性的Swift名称相同的Objective-C名称。然后,将willSet
和didSet
处理程序放在realistic属性上,该属性为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
private
,你将不得不弄乱你的班级与它的接口。但事实如此。
同样,我可能不会为你的特定用例做这个(如果你这样做,你显然可以在result
的set
中发出通知,而不是打扰willSet
和didSet
),但在某些情况下这可能是有用的,并且有一个答案描述如何作为参考,这是很好的。