为啥我的 NSNotification Observer 在消息运行时被释放?

Posted

技术标签:

【中文标题】为啥我的 NSNotification Observer 在消息运行时被释放?【英文标题】:Why is my NSNotificationObserver deallocated while a message runs on it?为什么我的 NSNotification Observer 在消息运行时被释放? 【发布时间】:2014-01-16 15:30:57 【问题描述】:

我有一种可能发生的情况,即在观察者处理传入通知时,对观察者的最后一个强引用被删除。

这会导致观察者立即被释放。我通常希望,当前正在运行的方法可以在对象被释放之前完成。这就是正常消息发送期间发生的情况。

代码的简化版本:

TKLAppDelegate.h:

#import <UIKit/UIKit.h>
#import "TKLNotificationObserver.h"

@interface TKLAppDelegate : UIResponder <UIApplicationDelegate>

@property (strong, nonatomic) TKLNotificationObserver *observer;

@end

TKLAppDelegate.m:

#import "TKLAppDelegate.h"

@implementation TKLAppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 

  // Create an observer and hold a strong reference to it in a property
  self.observer = [[TKLNotificationObserver alloc] init];

  // During the processing of this notification the observer will remove the only strong reference
  // to it and will immediatly be dealloced, before ending processing.
  [[NSNotificationCenter defaultCenter] postNotificationName:@"NotificationName" object:nil];

  // Create an observer and hold a strong reference to it in a property
  self.observer = [[TKLNotificationObserver alloc] init];

  // During the manual calling of the same method the observer will not be dealloced, because ARC still
  // holds a strong reference to the message reciever.
  [self.observer notificationRecieved:nil];

  return YES;


@end

TKLNotificationObserver.m:

#import "TKLNotificationObserver.h"
#import "TKLAppDelegate.h"

@implementation TKLNotificationObserver

- (id)init 
  if (self = [super init]) 
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(notificationRecieved:) name:@"NotificationName" object:nil];
  
  return self;


- (void)notificationRecieved:(NSNotification *)notification 
  [self doRemoveTheOnlyStrongReferenceOfThisObserver];
  NSLog(@"returing from notification Observer");


- (void)doRemoveTheOnlyStrongReferenceOfThisObserver 
  TKLAppDelegate * delegate = [[UIApplication sharedApplication] delegate];
  delegate.observer = nil;


- (void)dealloc 
  [[NSNotificationCenter defaultCenter] removeObserver:self name:@"NotificationName" object:nil];
  NSLog(@"dealloc was called");


@end

这样使用 App Delegate 风格不好,只做演示,实际代码不涉及 App Delegate。

输出是:

 dealloc was called
 returing from notification Observer
 returing from notification Observer
 dealloc was called

在第一种情况下,dealloc 在通知处理完成之前被调用。在第二种情况下,它的行为符合我的预期。

如果我在notificationReceived 中保持对self 的强引用,则dealloc 仅在处理后发生。我的期望是,ARC、运行时或其他为我保留此强参考的人。

我的代码有什么问题? 还是我的期望有问题? 是否有任何 Apple 或 Clang 提供的文档?

【问题讨论】:

对你来说是不是很奇怪,那个对象没有引用和解除分配,但是你把它从NSNotificationCenter中删除了?如果NSNotificationCenter 持有您的TKLNotificationObserver 的引用,则不应调用dealloc NSNotificationCenter 对观察者没有强引用。 【参考方案1】:

我的期望是,ARC、运行时或其他任何人保留它 对我来说很有参考价值。

事实并非如此,如Clang/ARC documentation 中所述:

Objective-C 方法的 self 参数变量实际上永远不会 由实施保留。这是未定义的行为,或者至少是 危险的,在消息发送期间导致对象被释放 到那个对象。

因此,如果调用doRemoveTheOnlyStrongReferenceOfThisObserver 可能有释放self的副作用,你必须使用 避免释放的临时强引用:

- (void)notificationRecieved:(NSNotification *)notification 
   typeof(self) myself = self;
   [self doRemoveTheOnlyStrongReferenceOfThisObserver];
   NSLog(@"returing from notification Observer");

更好的解决方案可能会避免这种副作用。

【讨论】:

这回答了我的问题。非常感谢。我没有找到那个。也许是因为我在寻找完全相反的结果。 是的,你是对的。在这种情况下,我必须重新考虑我的设计。【参考方案2】:

第一次解除分配可能发生在您两次设置 appDelegate 的观察者属性时,因此第一个实例在您第二次设置后立即解除分配

【讨论】:

没错,但是两个分配之间的通知是同步运行的,并且是执行第二个分配的时间,输出的第一行已经打印出来。

以上是关于为啥我的 NSNotification Observer 在消息运行时被释放?的主要内容,如果未能解决你的问题,请参考以下文章

React.js 和 webpack - 为啥它不允许 var、let、const?

Obj-C - NSNotification 执行两次?

NSNotification 与 UITextFieldDelegate

键入“NSNotification.Name?”没有成员“firInstanceIDTokenRefresh”

在科尔多瓦插件中设置 NSNotification 观察者

NSNotification NSMetadataQueryDidUpdate 获取不间断的通知消息