为啥我的 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?
NSNotification 与 UITextFieldDelegate