[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:  添加的通知,即使不移除,也不会崩溃,但是造成内存泄漏。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
iOS 中,可以使用通知NSNotification)机制来实现组件间的解耦和消息传递。通知是一种广播机制,一个组件可以发布(post)一个通知,其他组件可以订阅(observe)这个通知,并在接收到通知执行相应的操作。下面是一个通知的实例说明。 假设我们有一个应用程序,在其中有两个视图控制器 AViewController 和 BViewController。AViewController 中有一个按钮,当用户点击按钮,我们希望 BViewController 接收到一个通知,并在接收到通知更新界面。 首先,在 BViewController 中,我们需要注册一个通知观察者,代码如下: ``` - (void)viewDidLoad { [super viewDidLoad]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleNotification:) name:@"MyNotification" object:nil]; } - (void)dealloc { [[NSNotificationCenter defaultCenter] removeObserver:self]; } - (void)handleNotification:(NSNotification *)notification { // 处理通知 NSLog(@"Received notification: %@", notification); } ``` 在这里,我们使用 defaultCenter 对象注册了一个观察者,指定了通知的名称为 MyNotification。当通知被发布,handleNotification 方法被调用,并且可以在方法中处理通知。 接下来,在 AViewController 中,我们需要发布一个通知,代码如下: ``` - (IBAction)postNotification:(id)sender { [[NSNotificationCenter defaultCenter] postNotificationName:@"MyNotification" object:nil userInfo:@{@"key": @"value"}]; } ``` 在这里,我们使用 defaultCenter 对象发布了一个通知,指定了通知的名称为 MyNotification,没有指定通知的发送者,同可以携带一些额外的信息,例如 userInfo 字典中的键值对。 当用户点击 AViewController 中的按钮,就触发 postNotification 方法,同 BViewController 中的 handleNotification 方法也被调用,并且可以在其中处理通知。 总之,通知机制是一种非常方便的组件间通信方式,可以实现解耦和消息传递。在使用通知,需要注意及注册和注销观察者,并且指定通知的名称和携带的信息。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值