【问题标题】:NSNotificationCenter removeObserver:name:object: not removing observerNSNotificationCenter removeObserver:name:object: 不移除观察者
【发布时间】:2014-04-24 12:16:57
【问题描述】:

我在视图控制器中有一个方法可以设置一些通知:

- (void)processState
{
    MYGame *game = [[MYGameManager sharedInstance] getGameAtIndex:self.indexPath.row];

    if(game)
    {
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(notification_gameUpdated:) name:kMYNotificationGameUpdated object:game];
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(notification_gameEnded:) name:kMYNotificationGameEnded object:game];
    }
}

还有一个游戏更新方法,经常调用:

- (void)notification_gameUpdated:(NSNotification *)notification
{
    MYGame *game = notification.object;
    _game_status = (game.entity.bet.isWinning) ? MYGameStatusWin : MYGameStatusLose;
}

最后,当游戏结束时:

- (void)notification_gameEnded:(NSNotification *)notification
{
    MYGame *game = notification.object;

    // Clear the notifications   
    [[NSNotificationCenter defaultCenter] removeObserver:self name:kMYNotificationGameUpdated object:game];
    [[NSNotificationCenter defaultCenter] removeObserver:self name:kMYNotificationGameEnded object:game];

    self.gameIsActive = NO;
}

麻烦的是,即使我移除了观察者(断点显示这种情况正在发生),notification_gameUpdated: 方法仍然仍然被调用。如果我把它改成

[[NSNotificationCenter defaultCenter] removeObserver:self name:kMYNotificationGameUpdated object:nil];

这仍然无法清除它。但是如果我把它改成

[[NSNotificationCenter defaultCenter] removeObserver:self name:nil object:game];

那么确实清除它。一样

[[NSNotificationCenter defaultCenter] removeObserver:self];

但我也不热衷于这样做,因为我宁愿代码是干净的,并且如果我需要添加更多观察者,我不希望任何“陷阱”。我检查了其余的代码,找不到任何其他类向这个对象添加观察者,尽管其他视图控制器确实监听了相同的消息。

【问题讨论】:

  • processState 中添加一条日志消息,看看它是否被多次调用。
  • 我做到了,它只被调用过一次。
  • 游戏是否会覆盖 isEqual?
  • 嗯,你很可能会在那里找到一些东西。游戏对象扩展了一个名为 BaseModel 的类 - github.com/nicklockwood/BaseModel - 我不太了解它的作用,但它确实包含 encodeWithCoder: 之类的东西,所以我会咨询我的同事并报告! :)
  • 我发现了问题所在... 方法混乱!我会在这里将其标记为答案。抱歉浪费了大家的时间!

标签: objective-c nsnotificationcenter nsnotifications


【解决方案1】:

processState 是否被多次调用?这可以解释您所看到的行为。

如果是这样,解决此问题的一种方法是始终在添加侦听器之前删除它们。参见例如this answer.

【讨论】:

  • 不,processState 中的断点只被调用一次。
【解决方案2】:

编辑#2

尝试使用object:nil 注册,并在发布通知时在userInfo 字典中包含对game 的引用。然后,在接收器中,您可以与game 进行比较,并在匹配时执行您想要的任何操作。这应该会让您获得与使用 object:game 相同的行为,尽管它不能解释为什么您当前的实现不起作用


当您注册这样的通知时:

[[NSNotificationCenter defaultCenter] addObserver:self
                                         selector:@selector(notification_gameUpdated:)
                                             name:kMYNotificationGameUpdated 
                                           object:game];

@selector 只有在game 的特定实例是发送者时才会执行。 您是否有可能在注册后重新初始化game 的共享实例?这可能会导致您遇到的行为

尝试使用object:nil 注册通知,看看会发生什么。 (假设没有多个游戏同时运行)

【讨论】:

  • 绝对是同一个实例,我检查了哈希值。另外,我尝试了您的建议并将对象设置为nil(如上面的问题),它仍然没有删除观察者。
  • 抱歉,打错了!由于 'NDA' 风格的原因,我需要更改一些代码 :) 常量名称在我的代码中确实匹配。
  • 用另一个建议编辑...之后我没有想法
  • 好的,谢谢。至少我可以按照你的方式去做,虽然我宁愿不必做任何if 语句来检查通知中的数据......但除了你的建议之外,我的问题似乎很奇怪!
【解决方案3】:

原来问题的原因是 Method Swizzling。我正在处理的项目有addObserver:selector:name:object:removeObserver:name:object: swizzled。问题是尽管addObserver 已正确处理,removeObserver 仅在特定条件下删除对象。这显然需要改变......

但我将其发布为对其他人的警告......调酒可能会危害您的健康!

对浪费的时间表示歉意。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-06-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多