[iOS] NSNotificationCenter 不移除通知会崩溃吗

1. 问题导入 

由于平时写代码的习惯,每次在viewWillAppear:或init中添加通知后,都会在viewWillDisappear:或者dealloc里调用 removeObserver:name:object: 逐一移除通知。

如果没有移除通知,是否会崩溃,这问题在之前还真没有考虑过。

实践出真知,写个 Demo 测试一下。

2. Demo测试

NSNotificationCenter 添加通知的方式,有两种。

- (void)addObserver:(id)observer selector:(SEL)aSelector name:(nullable NSNotificationName)aName object:(nullable id)anObject;

- (id <NSObject>)addObserverForName:(nullable NSNotificationName)name object:(nullable id)obj queue:(nullable NSOperationQueue *)queue usingBlock:(void (^)(NSNotification *note))block;

下面对这两种添加逐一进行测试。

第一步,给 ViewController 添加跳转至 TestViewController 的按钮,和发送通知的按钮。

第二步,在 TestViewController 显示时添加通知,不需要在任何时机移除。

第三步,从 ViewController 进入 TestViewController,然后返回 ViewController,点击发送通知,确认是否崩溃。

// ViewController.m

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
}

- (IBAction)toTestButtonEvent:(id)sender {
    NSLog(@"%s", __FUNCTION__);
    
    // 跳转到TestViewController
    [self performSegueWithIdentifier:@"toTest" sender:nil];
}

- (IBAction)postButtonEvent:(id)sender {
    NSLog(@"%s", __FUNCTION__);
    
    // 发出通知
    [[NSNotificationCenter defaultCenter] postNotificationName:@"Test" object:nil];
}
// TestViewController.m

@interface TestViewController ()

@property(nonatomic, copy) NSString *string;

@end


@implementation TestViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    
    self.string = @"TestViewController";
}

- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
    
    // 第一种方式
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(testFunction) name:@"Test" object:nil];
    
    // 第二种方式
//    __weak typeof(self) weakSelf = self;
//    [[NSNotificationCenter defaultCenter] addObserverForName:@"Test" object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification * _Nonnull note) {
//        NSLog(@"%s", __FUNCTION__);
//
//        [weakSelf testFunction];
//    }];

}

- (void)viewWillDisappear:(BOOL)animated {
    [super viewWillDisappear:animated];
    
    // 移除通知
    // [[NSNotificationCenter defaultCenter] removeObserver:self name:@"Test" object:nil];
}

- (void)dealloc {
    NSLog(@"%s", __FUNCTION__);
}

- (void)testFunction {
    NSLog(@"%s", __FUNCTION__);
    
    NSLog(@"string: %@", self.string);
}

@end

3. 测试结果

// 第一种方式添加通知的测试结果
-[ViewController toTestButtonEvent:]
-[TestViewController dealloc]
-[ViewController postButtonEvent:]

-[ViewController toTestButtonEvent:]
-[TestViewController dealloc]
-[ViewController postButtonEvent:]

由测试结果可见,通过 addObserver:selector:name:object: 方式添加的通知,在 TestViewController 释放后,TestViewController 不会收到通知,也不会崩溃。

// 第二种方式添加通知的测试结果
-[ViewController toTestButtonEvent:]
-[TestViewController dealloc]
-[ViewController postButtonEvent:]
-[TestViewController viewWillAppear:]_block_invoke

-[ViewController toTestButtonEvent:]
-[TestViewController dealloc]
-[ViewController postButtonEvent:]
-[TestViewController viewWillAppear:]_block_invoke
-[TestViewController viewWillAppear:]_block_invoke

如果通过 addObserverForName:object:queue:usingBlock: 方式添加的通知,即使 TestViewController 已经被释放,仍然能够收到通知。由于 block 内使用的是 self 的弱引用对象,向空对象传递消息并不会导致崩溃。

然而从另一个方面考虑,这个 block 由于被 NSNotificationCenter 引用,无法被释放,造成内存泄漏。

4. 官方文档

其实,如果查看官方文档,就可以知道上文的测试结果并不精确。上文中只测试了 iOS 13 环境下 NSNotificationCenter 的表现,并没有对更早的版本进行测试。

搬运一下官方文档:


addObserver:selector:name:object:

https://developer.apple.com/documentation/foundation/nsnotificationcenter/1415360-addobserver?language=objc

If your app targets iOS 9.0 and later or macOS 10.11 and later, you don't need to unregister an observer in its dealloc method. Otherwise, you should call removeObserver:name:object: before observer or any object passed to this method is deallocated.

当处于 iOS 9.0 + 版本,通过该方法添加的通知,可以没必要在对象释放时移除通知。但是低于 iOS 9.0 版本时,就应该手动移除通知。


addObserverForName:object:queue:usingBlock: 

https://developer.apple.com/documentation/foundation/nsnotificationcenter/1411723-addobserverforname?language=objc

To unregister observations, you pass the object returned by this method to removeObserver:. You must invoke removeObserver: or removeObserver:name:object:before any object specified by addObserverForName:object:queue:usingBlock:is deallocated.

 使用该方法添加的通知,都必须手动移除。


5. 结论

  1. 当 iOS 9.0+ 时,不移除通知不会崩溃;当低于 iOS 9.0 时,需要手动移除通知。
  2. 使用 addObserverForName:object:queue:usingBlock:  添加的通知,即使不移除,也不会崩溃,但是造成内存泄漏。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值