NSNotificationCenter 循环引用

看别人博客内容中有写关于 [NSNotificationCenter defaultCenter]addObserverForName...存在内存泄露的可能. 自己写一个demo没有检测出来.

@interface TestViewController ()
@property (copy, nonatomic) NSString* aStr;
@end

- (void)viewDidLoad {
    [super viewDidLoad];
}

- (void)viewWillAppear:(BOOL)animated {
    
    [super viewWillAppear:animated];
    
    NSOperationQueue* queue = [[NSOperationQueue alloc]init];
    [[NSNotificationCenter defaultCenter]addObserverForName:UIKeyboardDidShowNotification object:nil queue:queue usingBlock:^(NSNotification * _Nonnull note) {
        self.aStr = @"JJJ";
    }];
    
    FBRetainCycleDetector *detector = [FBRetainCycleDetector new];
    [detector addCandidate:self];
    NSSet *retainCycles = [detector findRetainCycles];
    NSLog(@"%@", retainCycles); //利用FBRetainCycleDetector 却检测不出来内存泄露.但是 dealloc方法在pop的时候没有执行,哪位朋友知道这是为什么吗?
}

- (void)dealloc{
    NSLog(@"dealloc method excute!");
}
阅读 5.1k
4 个回答

利用FBRetainCycleDetector 却检测不出来内存泄露是因为根本就没有循环引用嘛。

[NSNotificationCenter defaultCenter] 强引用 usingBlockusingBlock中强引用 self

self 在被 pop 之后并不会被销毁是因为被 [NSNotificationCenter defaultCenter] 间接强引用。

NSNotificationCenterdefaultCenter会创建一个static的全局对象, 全局对象的usingBlock会强引用self这个TestViewController控制器, 所以self在pop后不能被释放.


相应的, [[NSNotificationCenter defaultCenter] addObserver:self selector:name: 会把observer存到对象的observers数组中, 每当有postnotification时, 遍历observers数组对象发送通知消息, 如果observer被释放置为nil, App就会奔溃.

持有链

NSNotificationCenter -> callbackBlock -> self

由于defaultCenter是单例(全局变量),会始终持有self直至调用removeObserver,所以self无法被释放。

可以看看文档中对这个接口的解释。

The block is copied by the notification center and (the copy) held until the observer registration is removed.

另外需要注意,这个方法是有返回值的,返回值用于后续的删除通知。

- (id <NSObject>)addObserverForName:(nullable NSNotificationName)name object:(nullable id)obj queue:(nullable NSOperationQueue *)queue usingBlock:(void (^)(NSNotification *note))block NS_AVAILABLE(10_6, 4_0);
    // The return value is retained by the system, and should be held onto by the caller in
    // order to remove the observer with removeObserver: later, to stop observation.

实际不存在循环引用,所以用内存检查工具是检测不到的.
使用的时候你可以这样:
__weak typeof(self) weakSelf = self;

[[NSNotificationCenter defaultCenter]addObserverForName:UIKeyboardDidHideNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification * _Nonnull note) {
    __strong typeof(weakSelf) strongSelf = weakSelf;
    [strongSelf doSomeThing];
    
}];

//不用移除observer也是可以的.

还可以这样
//self.observer 是用Strong修饰.

self.observer = [[NSNotificationCenter defaultCenter]addObserverForName:UIKeyboardDidHideNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification * _Nonnull note) {
    __strong typeof(weakSelf) strongSelf = weakSelf;
    [strongSelf doSomeThing];
    
}];

需要移除观察者
[[NSNotificationCenter defaultCenter]removeObserver:self.observer];
//如果self.observer 是strong修饰需要置为nil,weak是就不需要.
self.observer = nil;
//引用关系
[NSNotificationCenter defaultCenter]-->Observer --CopyBlock-->Self

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题