0

文档中,它说:

该块由通知中心复制并保留(副本)直到观察者注册被删除。

它提供了一个一次性观察者示例代码,如下所示:

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被保留并没有被删除。

到底是怎么回事?

4

3 回答 3

0

确认发生了一些奇怪的行为。

首先,在观察者被移除之前,我在成功的观察者闭包上放置了一个断点,并打印了令牌的内存地址,并且NotificationCenter.default. 打印NotificationCenter.default显示已注册的观察员。

由于列表很长,我不会在这里发布日志。顺便说一句,self在关闭中被弱捕获。

Printing description of successToken:
▿ Optional<NSObject>
  - some : <__NSObserver: 0x60000384e940>
Printing description of failureToken:
▿ Optional<NSObject>
  - some : <__NSObserver: 0x60000384ea30>

还确认在调用 sNotificationCenter.default后再次打印(据说)删除了观察者。removeObserver(_:)

接下来,我离开视图控制器并确认self报价代码中的 已解除分配。

最后,我打开调试内存图并搜索内存地址,发现:

在此处输入图像描述

最后,没有保留周期。只是观察者没有被移除,并且由于闭包是活着的,所以被捕获self的对象在其生命周期之外还活着。

如果你们认为这是一个错误,请发表评论。根据上的文档NotificationCenter,它很可能是......

于 2019-06-27T11:36:42.147 回答
0

最近我自己也遇到了类似的问题。

这似乎不是一个错误,而是令牌的未记录功能(正如您已经注意到的)是 __NSObserver 类型。仔细观察该类型,您会发现它包含对块的引用。由于您的块对令牌本身具有强引用(通过可选的 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)
}
于 2020-04-16T11:13:15.427 回答
-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)
}
于 2019-06-27T07:00:28.777 回答