在documentation中,它表示:
该块将由通知中心复制并保留(副本) 直到观察者注册被删除。
它提供了一个一次性的观察者示例代码,如下所示:
let center = NSNotificationCenter.defaultCenter()
let mainQueue = NSOperationQueue.mainQueue()
var token: NSObjectProtocol?
token = center.addObserverForName("OneTimeNotification", object: nil, queue: mainQueue) { (note) in
print("Received the notification!")
center.removeObserver(token!)
}
现在我希望在调用removeObserver(_:)
时删除观察者,所以我的代码如下:
let nc = NotificationCenter.default
var successToken: NSObjectProtocol?
var failureToken: NSObjectProtocol?
successToken = nc.addObserver(
forName: .ContentLoadSuccess,
object: nil,
queue: .main)
{ (_) in
nc.removeObserver(successToken!)
nc.removeObserver(failureToken!)
self.onSuccess(self, .contentData)
}
failureToken = nc.addObserver(
forName: .ContentLoadFailure,
object: nil,
queue: .main)
{ (_) in
nc.removeObserver(successToken!)
nc.removeObserver(failureToken!)
guard case .failed(let error) = ContentRepository.state else {
GeneralError.invalidState.record()
return
}
self.onFailure(self, .contentData, error)
}
令人惊讶的是,self
被保留而不被删除。
这是怎么回事?
答案 0 :(得分:0)
确认正在发生一些奇怪的行为。
首先,在删除观察者之前,我在成功观察者关闭处设置了一个断点,并打印了令牌和NotificationCenter.default
的内存地址。打印NotificationCenter.default
将显示已注册的观察者。
由于列表很长,因此我不会在此处发布日志。
顺便说一下,self
在关闭中被弱捕获了。
Printing description of successToken:
▿ Optional<NSObject>
- some : <__NSObserver: 0x60000384e940>
Printing description of failureToken:
▿ Optional<NSObject>
- some : <__NSObserver: 0x60000384ea30>
还确认(据说)通过调用NotificationCenter.default
之后再次打印removeObserver(_:)
删除了观察者。
接下来,我离开视图控制器,并确认已取消分配报价代码中的self
。
最后,我打开调试内存图并搜索内存地址,发现了这一点:
最后,没有保留周期。只是观察者没有被移除,并且因为关闭是有效的,所以捕获的self
在其生命周期之外仍然有效。
如果你们认为这是一个错误,请发表评论。根据{{1}}上的文档,很可能是...
答案 1 :(得分:0)
最近,我本人也遇到了类似的问题。
这似乎不是一个错误,而是令牌的未记录的功能(如您已经注意到的)是__NSObserver类型的。 Looking closer at that type,您会看到它包含对块的引用。由于您的代码块(通过可选的var)拥有对令牌本身的强烈引用,因此您有一个循环。
尝试在使用可选令牌参考后将其设置为nil:
let nc = NotificationCenter.default
var successToken: NSObjectProtocol?
var failureToken: NSObjectProtocol?
successToken = nc.addObserver(
forName: .ContentLoadSuccess,
object: nil,
queue: .main)
{ (_) in
nc.removeObserver(successToken!)
nc.removeObserver(failureToken!)
successToken = nil // Break reference cycle
failureToken = nil
self.onSuccess(self, .contentData)
}
答案 2 :(得分:-1)
您需要对self
使用弱引用,如下所示:
let nc = NotificationCenter.default
var successToken: NSObjectProtocol?
var failureToken: NSObjectProtocol?
successToken = nc.addObserver(
forName: .ContentLoadSuccess,
object: nil,
queue: .main)
{[weak self] (_) in
guard let strongSelf = self else { return }
nc.removeObserver(successToken!)
nc.removeObserver(failureToken!)
strongSelf.onSuccess(strongSelf, .contentData)
}
failureToken = nc.addObserver(
forName: .ContentLoadFailure,
object: nil,
queue: .main)
{[weak self] (_) in
guard let strongSelf = self else { return }
nc.removeObserver(successToken!)
nc.removeObserver(failureToken!)
guard case .failed(let error) = ContentRepository.state else {
GeneralError.invalidState.record()
return
}
strongSelf.onFailure(strongSelf, .contentData, error)
}