NSnotificationCenter 正确使用姿势, removeObject 探索
最近在做平板的过程中,发现了一些很不规范的代码。偶然修复支付bug的时候,看到其他项目代码,使用通知的地方没有移除,我以为我这个模块的支付闪退是因为他通知没有移除的缘故。而在debug和看了具体的代码的时候才发现和这里没有关系。在我印象中,曾经因为没有移除通知而遇到闪退的问题。所以让我很意外,于是写了个demo研究了下,同时来讲下NSNotificationCenter使用的正确姿势。
NSNotificationCenter
对于这个没必要多说,就是一个消息通知机制,类似广播。观察者只需要向消息中心注册感兴趣的东西,当有地方发出这个消息的时候,通知中心会发送给注册这个消息的对象。这样也起到了多个对象之间解耦的作用。苹果给我们封装了这个NSNotificationCenter,让我们可以很方便的进行通知的注册和移除。然而,有些人的姿势还是有点小问题的,下面就看看正确的姿势吧!
正确姿势之remove
只要往NSNotificationCenter注册了,就必须有remove的存在,这点是大家共识的。但是大家在使用的时候发现,在UIViewController 中addObserver后没有移除,好像也没有挂!我想很多人可能和我有一样的疑问,是不是因为使用了ARC?在你对象销毁的时候自动置为nil了呢?或者苹果在实现这个类的时候用了什么神奇的方式呢?下面我们就一步步来探究下。
首先,向NSNotificationCenter中addObserver后,并没有对这个对象进行引用计数加1操作,所以它只是保存了地址。为了验证这个操作,我们来做下代码的测试。
一个测试类,用来注册通知:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
@implementation MRCObject- (id)init{ if (self = [ super init]) { [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(test) name:@"test" object:nil]; } return self;}- (void)test{ NSLog(@ "=================" );}- (void)dealloc{ [ super dealloc];} |
这个类很简单,就是在初始化的时候,给他注册一个通知。但是在销毁的时候不进行remove操作。我们在VC中创建这个对象后,然后销毁,最后发送这个通知:
|
1
2
3
4
5
6
7
8
|
- (void)viewDidLoad { [ super viewDidLoad]; MRCObject *obj = [[MRCObject alloc] init]; [obj release]; [[NSNotificationCenter defaultCenter] postNotificationName:@ "test" object:nil];} |
在进入这个vc后,我们发现挂了。。而打印出的信息是:
|
1
|
2015-01-19 22:49:06.655 测试[1158:286268] *** -[MRCObject test]: message sent to deallocated instance 0x17000e5b0 |
我们可以发现,向野指针对象发送了消息,所以挂掉了。从这点来看,苹果实现也基本差不多是这样的,只保存了个对象的地址,并没有在销毁的时候置为nil。
这点就可以证明,addObserver后,必须要有remove操作。
现在我们在UIViewController中注册通知,不移除,看看会不会挂掉。
|
1
2
3
4
|
- (void)viewDidLoad { [ super viewDidLoad]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(test) name:@ "test" object:nil];} |
首先用navigationController进入到这个页面,然后pop出去。最后点击发送通知的按钮事件:
|
1
2
3
4
|
- (void)didButtonClicked:(id)sender{ [[NSNotificationCenter defaultCenter] postNotificationName:@ "test" object:nil];} |
无论你怎么点击这个按钮,他就是不挂!这下,是不是很郁闷了?我们可以找找看,你代码里面没有remove操作,但是NSNotificationCenter那边已经移除了,不然肯定会出现上面野指针的问题。看来看去,也只能说明是UIViewController自己销毁的时候帮我们暗地里移除了。
那我们如何证明呢?由于我们看不到源码,所以也不知道有没有调用。这个时候,我们可以从这个通知中心下手!!!怎么下手呢?我只要证明UIViewController在销毁的时候调用了remove方法,就可以证明我们的猜想是对的了!这个时候,就需要用到我们强大的类别这个特性了。我们为NSNotificationCenter添加个类别,重写他的- (void)removeObserver:(id)observer方法:
|
1
2
3
4
|
- (void)removeObserver:(id)observer{ NSLog(@ "====%@ remove===" , [observer class]);} |
这样在我们VC中导入这个类别,然后pop出来,看看发生了什么!
|
1
|
2015-01-19 22:59:00.580 测试[1181:288728] ====TestViewController remove=== |
怎么样?是不是可以证明系统的UIViewController在销毁的时候调用了这个方法。(不建议大家在开发的时候用类别的方式覆盖原有的方法,由于类别方法具有更高的优先权,所以有可能影响到其他地方。这里只是调试用)。
以上也提醒我们,在你不是销毁的时候,千万不要直接调用[[NSNotificationCenter defaultCenter] removeObserver:self]; 这个方法,因为你有可能移除了系统注册的通知。
正确姿势之注意重复addObserver
在我们开发中,我们经常可以看到这样的代码:
|
1
2
3
4
5
6
7
8
9
10
11
|
- (void)viewWillAppear:(BOOL)animated{ [ super viewWillAppear:animated]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(test) name:@ "test" object:nil];}- (void)viewWillDisappear:(BOOL)animated{ [ super viewWillDisappear:animated]; [[NSNotificationCenter defaultCenter] removeObserver:self name:@ "test" object:nil];} |
就是在页面出现的时候注册通知,页面消失时移除通知。你这边可要注意了,一定要成双成对出现,如果你只在viewWillAppear 中 addObserver没有在viewWillDisappear 中 removeObserver那么当消息发生的时候,你的方法会被调用多次,这点必须牢记在心。
正确姿势之多线程通知
首先看下苹果的官方说明:
Regular notification centers deliver notifications on the thread in which the notification was posted. Distributed notification centers deliver notifications on the main thread. At times, you may require notifications to be delivered on a particular thread that is determined by you instead of the notification center. For example, if an object running in a background thread is listening for notifications from the user interface, such as a window closing, you would like to receive the notifications in the background thread instead of the main thread. In these cases, you must capture the notifications as they are delivered on the default thread and redirect them to the appropriate thread.
意思很简单,NSNotificationCenter消息的接受线程是基于发送消息的线程的。也就是同步的,因此,有时候,你发送的消息可能不在主线程,而大家都知道操作UI必须在主线程,不然会出现不响应的情况。所以,在你收到消息通知的时候,注意选择你要执行的线程。下面看个示例代码
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
//接受消息通知的回调- (void)test{ if ([[NSThread currentThread] isMainThread]) { NSLog(@ "main" ); } else { NSLog(@ "not main" ); } dispatch_async(dispatch_get_main_queue(), ^{ //do your UI });}//发送消息的线程- (void)sendNotification{ dispatch_queue_t defaultQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_async(defaultQueue, ^{ [[NSNotificationCenter defaultCenter] postNotificationName:@ "test" object:nil]; });} |
总结
通知平常使用的知识点差不多就这么多。希望对大家有帮助。最后,代码一定要养成良好的习惯,该移除的还是要移除。
copy from JamesYu
NSnotificationCenter 正确使用姿势, removeObject 探索的更多相关文章
- xpath轴的正确使用姿势
网上看了许多关于轴的介绍,只介绍了语法,而没有明说具体实际中该怎么使用,百思不得其解. 背景--python中使用xpath: ----------------------------------- ...
- 高版本jquery尤其是1.10.2的版本设置input radio设置值的最正确的姿势。
$("input:radio[name="analyshowtype"]").attr("checked",false); $(" ...
- 微信H5中静默登录及非静默登录的正确使用姿势
在微信中打开网页且需要调用微信登录接口时,微信官方给我们提供了两种登录调用方式:静默登录和非静默登录:但是官方文档中却没有说明在何种情况下使用静默登录,何种情况下使用非静默登录,所以在这里,我想将之前 ...
- Java日志正确使用姿势
前言 关于日志,在大家的印象中都是比较简单的,只须引入了相关依赖包,剩下的事情就是在项目中“尽情”的打印我们需要的信息了.但是往往越简单的东西越容易让我们忽视,从而导致一些不该有的bug发生,作为一名 ...
- MongoDB系列:五、MongoDB Driver使用正确的姿势连接复制集
MongoDB复制集(Replica Set)通过存储多份数据副本来保证数据的高可靠,通过自动的主备切换机制来保证服务的高可用.但需要注意的时,连接副本集的姿势如果不对,服务高可用将不复存在. 使用复 ...
- 基于winserver的Apollo配置中心分布式&集群部署实践(正确部署姿势)
基于winserver的Apollo配置中心分布式&集群部署实践(正确部署姿势) 前言 前几天对Apollo配置中心的demo进行一个部署试用,现公司已决定使用,这两天进行分布式部署的时候 ...
- 玩转java多线程(wait和notifyAll的正确使用姿势)
转载请标明博客的地址 本人博客和github账号,如果对你有帮助请在本人github项目AioSocket上点个star,激励作者对社区贡献 个人博客:https://www.cnblogs.com/ ...
- Gradle的依赖方式——Lombok在Gradle中的正确配置姿势
写过java的都知道,lombok几乎在项目中处于不可或缺的一部分,但是lombok在Gradle的项目中配置并非人人都知道. 很多人在项目依赖中直接这样写 1 compile "org.p ...
- Android 线程的正确使用姿势
进程优先级(Process Priority) 线程寄宿在进程当中,线程的生命周期直接被进程所影响,而进程的存活又和其优先级直接相关.在处理进程优先级的时候,大部分人靠直觉都能知道前台进程(Foreg ...
随机推荐
- BZOJ 2329: [HNOI2011]括号修复( splay )
把括号序列后一定是))))((((这种形式的..所以维护一个最大前缀和l, 最大后缀和r就可以了..答案就是(l+1)/2+(r+1)/2...用splay维护,O(NlogN). 其实还是挺好写的, ...
- Vmware虚拟机时间不准问题
测试程序时碰到虚拟机经常时间不准,深受困扰,后来发现虚拟机有一个设置可以同步虚拟机和宿主机的时间: 该功能需要vmware tools安装成功才能有效.vmware tools的安装就不多细说了,至于 ...
- win7下Java环境变量配置及说明
在官网上下载与操作系统对应的JDK(http://www.oracle.com/index.html) 比如我的安装路径是E:\jdk1.7.0_51\JDK 右击计算机---->属性----& ...
- SPSS19.0实战之聚类分析
这篇文章与上一篇的回归分析是一次实习作业整理出来的.所以参考文献一并放在该文最后.CNBlOG网页排版太困难了,又不喜欢live writer…… 聚类分析是将物理或者抽象对象的集合分成相似的对象类的 ...
- ECMAScript 5中新增的数组方法
ECMAScript 5中定义了9个新的数组方法,用于遍历.映射.过滤.检测.简化和搜索数组. 在开始介绍之前,很有必要对这几个新增的数组方法做一个概述.首先,大多数方法的第一个参数接收一个函数,并且 ...
- CPU卡
CPU卡芯片通俗地讲就是指芯片内含有一个微处理器,它的功能相当于一台微型计算机.人们经常使用的集成电路卡(IC卡)上的金属片就是CPU卡芯片.CPU卡可适用于金融.保险.交警.政府行业等多个领域,具有 ...
- HDOJ 1253 胜利大逃亡(bfs)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1253 思路分析:因为问题需要寻找到达终点的最短的距离(最短的步数),即在状态转换图上需要找出层次最浅的 ...
- UIPickView之自定义生日键盘和城市键盘
//// ViewController.m// 04-键盘处理// // #import "ViewController.h"#import "XMGProvince ...
- Cloud Foundry warden container 安全性探讨
本文将从Cloud Foundry中warden container的几个方面探讨warden container的安全性. 1. warden container互訪 1.1. 互訪原理· 在Cl ...
- DevExpress ASP.NET 使用经验谈(3)-XPO对象的使用(使用指定数据连接)
首先,我们贴出上一节Users类XPO对象的保存代码,直接建立的XPO Session会话,因为没有与我们所期望的数据层建立绑定, 所以程序自动创建了一个Access数据库,作为默认数据库操作对象来使 ...