NSNotificationCenter

对于这个没必要多说,就是一个消息通知机制,类似广播。观察者只需要向消息中心注册感兴趣的东西,当有地方发出这个消息的时候,通知中心会发送给注册这个消息的对象。这样也起到了多个对象之间解耦的作用。苹果给我们封装了这个NSNotificationCenter,让我们可以很方便的进行通知的注册和移除。然而,有些人的姿势还是有点小问题的,下面就看看正确的姿势吧!

正确姿势之remove

只要往NSNotificationCenter注册了,就必须有remove的存在,这点是大家共识的。但是大家在使用的时候发现,在UIViewController 中addObserver后没有移除,好像也没有挂!我想很多人可能和我有一样的疑问,是不是因为使用了ARC?在你对象销毁的时候自动置为nil了呢?或者苹果在实现这个类的时候用了什么神奇的方式呢?下面我们就一步步来探究下。

首先,向NSNotificationCenter中addObserver后,并没有对这个对象进行引用计数加1操作,所以它只是保存了地址。为了验证这个操作,我们来做下代码的测试。

一个测试类,用来注册通知:

@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];
}

@end

这个类很简单,就是在初始化的时候,给他注册一个通知。但是在销毁的时候不进行remove操作。我们在VC中创建这个对象后,然后销毁,最后发送这个通知:

- (void)viewDidLoad {
    [super viewDidLoad];

    MRCObject *obj = [[MRCObject alloc] init];
    [obj release];

    [[NSNotificationCenter defaultCenter] postNotificationName:@"test" object:nil];
}

在进入这个vc后,我们发现挂了。。而打印出的信息是:

2015-01-19 22:49:06.655 测试[1158:286268] *** -[MRCObject test]: message sent to deallocated instance 0x17000e5b0

我们可以发现,向野指针对象发送了消息,所以挂掉了。从这点来看,苹果实现也基本差不多是这样的,只保存了个对象的地址,并没有在销毁的时候置为nil。

这点就可以证明,addObserver后,必须要有remove操作。

现在我们在UIViewController中注册通知,不移除,看看会不会挂掉。

- (void)viewDidLoad {
    [super viewDidLoad];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(test) name:@"test" object:nil];
}

首先用navigationController进入到这个页面,然后pop出去。最后点击发送通知的按钮事件:

- (void)didButtonClicked:(id)sender
{
   [[NSNotificationCenter defaultCenter] postNotificationName:@"test" object:nil];
}

无论你怎么点击这个按钮,他就是不挂!这下,是不是很郁闷了?我们可以找找看,你代码里面没有remove操作,但是NSNotificationCenter那边已经移除了,不然肯定会出现上面野指针的问题。看来看去,也只能说明是UIViewController自己销毁的时候帮我们暗地里移除了。

那我们如何证明呢?由于我们看不到源码,所以也不知道有没有调用。这个时候,我们可以从这个通知中心下手!!!怎么下手呢?我只要证明UIViewController在销毁的时候调用了remove方法,就可以证明我们的猜想是对的了!这个时候,就需要用到我们强大的类别这个特性了。我们为NSNotificationCenter添加个类别,重写他的- (void)removeObserver:(id)observer方法:

- (void)removeObserver:(id)observer
{
   NSLog(@"====%@ remove===", [observer class]);
}

这样在我们VC中导入这个类别,然后pop出来,看看发生了什么!

2015-01-19 22:59:00.580 测试[1181:288728] ====TestViewController remove===

怎么样?是不是可以证明系统的UIViewController在销毁的时候调用了这个方法。(不建议大家在开发的时候用类别的方式覆盖原有的方法,由于类别方法具有更高的优先权,所以有可能影响到其他地方。这里只是调试用)。

以上也提醒我们,在你不是销毁的时候,千万不要直接调用[[NSNotificationCenter defaultCenter] removeObserver:self]; 这个方法,因为你有可能移除了系统注册的通知。

正确姿势之注意重复addObserver

在我们开发中,我们经常可以看到这样的代码:

- (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必须在主线程,不然会出现不响应的情况。所以,在你收到消息通知的时候,注意选择你要执行的线程。下面看个示例代码

//接受消息通知的回调
- (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];
   });
}

iOS监听模式系列之NSNotificationCenter的简单使用的更多相关文章

  1. iOS监听模式系列之通知中心

    补充--通知中心 对于很多初学者往往会把iOS中的本地通知.推送通知和iOS通知中心的概念弄混.其实二者之间并没有任何关系,事实上它们都不属于一个框架,前者属于UIKit框架,后者属于Foundati ...

  2. iOS监听模式系列之关于delegate(代理,委托)的学习

    首先,大家应该都明白的是委托是协议的一种,顾名思义,就是委托他人帮自己去做什么事.也就是当自己做什么事情不方便的时候,就可以建立一个委托,这样就可以委托他人帮自己去实现什么方法. 其次,我简单的总结了 ...

  3. iOS监听模式系列之键值编码KVC、键值监听KVO的简单介绍和应用

    键值编码KVC 我们知道在C#中可以通过反射读写一个对象的属性,有时候这种方式特别方便,因为你可以利用字符串的方式去动态控制一个对象.其实由于ObjC的语言特性,你根部不必进行任何操作就可以进行属性的 ...

  4. iOS监听模式系列之IOS中的几中观察监听模式

    本文介绍Objective C中实现观察者模式(也被称为广播者/监听者.发布/注册或者通知)的五种方法以及每种方法的价值所在. 该文章将包括: 1 手动广播者和监听者(Broadcaster and ...

  5. iOS监听模式系列之iOS开发证书、秘钥

    补充--iOS开发证书.秘钥 iOS开发过程中如果需要进行真机调试.发布需要注册申请很多证书,对于初学者往往迷惑不解,再加上今天的文章中会牵扯到一些特殊配置,这里就简单的对iOS开发的常用证书和秘钥等 ...

  6. iOS监听模式系列之推送消息通知

    推送通知 和本地通知不同,推送通知是由应用服务提供商发起的,通过苹果的APNs(Apple Push Notification Server)发送到应用客户端.下面是苹果官方关于推送通知的过程示意图: ...

  7. iOS监听模式系列之本地通知Notification

    本地通知 本地通知是由本地应用触发的,它是基于时间行为的一种通知形式,例如闹钟定时.待办事项提醒,又或者一个应用在一段时候后不使用通常会提示用户使用此应用等都是本地通知.创建一个本地通知通常分为以下几 ...

  8. iOS监听模式系列之对APNs的认知与理解

    前言: APNs 协议在近两年的 WWDC 上改过两次, 15 年 12 月 17 日更是推出了革命性的新特性.但在国内传播的博客.面试题里关于 APNs 的答案全都是旧的.错的. 导航: 对 APN ...

  9. iOS 监听 出发 Home键 NSNotificationCenter UIApplicationWillResignActiveNotification

    第一步: 创建2个NSNotificationCenter监听 [[NSNotificationCenter defaultCenter] addObserver:self selector:@sel ...

随机推荐

  1. Python 表示无穷大的数

    我之前只知道设置初始值0.今天偶然在Python算法书上看到这个片段,从100个随机数里面找2个最靠近的自然数(不相等): from random import randrange seq = [ra ...

  2. Linux动态频率调节系统CPUFreq之三:governor

    在上一篇文章中,介绍了cpufreq的core层,core提供了cpufreq系统的初始化,公共数据结构的建立以及对cpufreq中其它子部件提供注册功能.core的最核心功能是对policy的管理, ...

  3. Linux动态频率调节系统CPUFreq之一:概述

    随着技术的发展,我们对CPU的处理能力提出了越来越高的需求,芯片厂家也对制造工艺不断地提升.现在的主流PC处理器的主频已经在3GHz左右,就算是智能手机的处理器也已经可以工作在1.5GHz以上,可是我 ...

  4. SQLite 表达式(http://www.w3cschool.cc/sqlite/sqlite-expressions.html)

    SQLite 表达式 表达式是一个或多个值.运算符和计算值的SQL函数的组合. SQL 表达式与公式类似,都写在查询语言中.您还可以使用特定的数据集来查询数据库. 语法 假设 SELECT 语句的基本 ...

  5. 深入浅出seesion和cookie

    session在计算机中,尤其是在网络应用中,称为"会话控制".session 对象存储特定用户会话所需的属性及配置信息.session跟踪是Web程序中常用的技术,用来跟踪用户的 ...

  6. 学习Tensorflow,反卷积

    在深度学习网络结构中,各个层的类别可以分为这几种:卷积层,全连接层,relu层,pool层和反卷积层等.目前,在像素级估计和端对端学习问题中,全卷积网络展现了他的优势,里面有个很重要的层,将卷积后的f ...

  7. API创建员工联系人

    DECLARE ln_contact_rel_id PER_CONTACT_RELATIONSHIPS.CONTACT_RELATIONSHIP_ID%TYPE; ln_ctr_object_ver_ ...

  8. 最简单的基于DirectShow的示例:视频播放器图形界面版

    ===================================================== 最简单的基于DirectShow的示例文章列表: 最简单的基于DirectShow的示例:视 ...

  9. java 之容器

    在Java中,我们想要保存对象可以使用很多种手段.我们之前了解过的数组就是其中之一.但是数组具有固定的尺寸,而通常来说,程序总是在运行时根据条件来创建对象,我们无法预知将要创建对象的个数以及类型,所以 ...

  10. iOS 图片裁剪与修改

    最近做的项目中需要上传头像,发表内容的时候也要涉及到图片上传,我直接用的原图上传,但是由于公司网络差,原图太大,老是加载好久好久,所以需要把原图裁剪或者修改分辨率之后再上传,找了好久,做了很多尝试才解 ...