iOS的通知相关知识

我们知道,两个对象的传值有两种:正向传值反向传值。
其中,正向传值可以直接将值赋值过去完成传值动作。
而反向传值,一般有三种方法:代理、block和通知
其中,代理和block都是一对一的传值,而涉及到一对多或者是层次结构比较深的情况下,我们一般使用通知

通知是以[NSNotificationCenter defaultCenter]单例形式存在。
通知一般需要做三步:

  1. 监听通知(又被称为注册通知)
  2. 发送通知
  3. 移除通知

需要注意的是,监听通知需要在发送通知前执行,不然通知监听不到。

通知的通信过程
首先我们介绍一下通知的使用过程:

1. 监听通知(注册通知)

监听通知有两个方法,分别为:

监听方法一:
observer:监听者,一般为self
aSelector:监听后执行的方法
NSNotificationName:通知名称
object:监听哪个对象发出的通知,如果使用"nil"值,代表监听所有通知。
*/
- (void)addObserver:(id)observer selector:(SEL)aSelector name:(nullable NSNotificationName)aName object:(nullable id)anObject;

监听方法二:
/**
NSNotificationName:通知名称
object:监听哪个对象发出的通知,如果使用"nil"值,代表监听所有通知。
queue:决定block在哪个线程中执行,nil代表在发送通知的线程执行
block:监听到通知就调用这个block
*/
- (id <NSObject>)addObserverForName:(nullable NSNotificationName)name object:(nullable id)obj queue:(nullable NSOperationQueue *)queue usingBlock:(void (^)(NSNotification *note))block API_AVAILABLE(macos(10.6), ios(4.0), watchos(2.0), tvos(9.0));

2. 发送通知

发送通知也有两个方法

/**
NSNotificationName:通知名称
object:谁发出的通知,nil为匿名发送
*/
- (void)postNotificationName:(NSNotificationName)aName object:(nullable id)anObject;

/**
NSNotificationName:通知名称
object:谁发出的通知,nil为匿名发送
userInfo:通知中带的参数
*/
- (void)postNotificationName:(NSNotificationName)aName object:(nullable id)anObject userInfo:(nullable NSDictionary *)aUserInfo;

举一个带值通知的例子:

NSDictionary *dice = @{@"color": [UIColor redColor]};
[[NSNotificationCenter defaultCenter] postNotificationName:@"changeBgColor" object:nil userInfo:dict];

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(changeBgColor:) name:@"changeBgColor" object:nil];

- (void)changeBgColor:(NSNotification *)notification{
     NSLog(@"接受到通知,改变背景颜色"); // 如果是传多个数据,那么需要哪个数据,就对应取出对应的数据即可                        
     self.view.backgroundColor = notification.userInfo[@"color"];
}

3. 移除通知

- (void)dealloc
{
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}

需要知道的是,在iOS9.0以前,观察者模式的移除是必须写的,不写会造成野指针错误。而在iOS9.0以后,观察者模式的移除成为了非必须,不写也不会造成崩溃。这是为什么呢?
这是因为在iOS9.0之前,通知中心对观察对象是以unsafe_unretained持有的,在使用完毕后,观察对象不会主动置为nil,如果不主动进行移除,则会造成野指针错误。而在iOS9.0以后,通知中心对观察对象的持有换成了weak,在使用完毕后,会主动置为nil,从而不会造成野指针,也就不需要程序员手动进行移除通知。


通知在多线程中的调用

在以

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

这种方式监听通知的情况下,收到到通知后,执行的方法所在的线程与发送通知的线程有关
无非四种情况:
1发送方在主线程,接收方在主线程;
2发送方在主线程,接收方在子线程;
3发送方在子线程,接收方在主线程;
4发送方在子线程,接收方在子线程;
我们分别做验证:

1. 发送方在主线程,接收方在主线程
发送方
dispatch_async(dispatch_get_main_queue(), ^{
        NSLog(@"发送方-[NSThread currentThread] - %@", [NSThread currentThread]);
        [[NSNotificationCenter defaultCenter] postNotificationName:@"kNotificationName" object:nil];
    });

监听方
dispatch_async(dispatch_get_main_queue(), ^{
        NSLog(@"接收方-[NSThread currentThread] - %@", [NSThread currentThread]);
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(test) name:@"kNotificationName" object:nil];
    });

- (void)test
{
    NSLog(@"test-[NSThread currentThread] - %@", [NSThread currentThread]);
}

打印结果:
接收方-[NSThread currentThread] - <NSThread: 0x600003a511c0>{number = 1, name = main}
发送方-[NSThread currentThread] - <NSThread: 0x600003a511c0>{number = 1, name = main}
test-[NSThread currentThread] - <NSThread: 0x600003a511c0>{number = 1, name = main}

2. 发送方在主线程,接收方在子线程
发送方
dispatch_async(dispatch_get_main_queue(), ^{
        NSLog(@"发送方-[NSThread currentThread] - %@", [NSThread currentThread]);
        [[NSNotificationCenter defaultCenter] postNotificationName:@"kNotificationName" object:nil];
    });

监听方
dispatch_async(dispatch_get_global_queue(0, 0), ^{
        NSLog(@"接收方-[NSThread currentThread] - %@", [NSThread currentThread]);
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(test) name:@"kNotificationName" object:nil];
    });

- (void)test
{
    NSLog(@"test-[NSThread currentThread] - %@", [NSThread currentThread]);
}

打印结果:
接收方-[NSThread currentThread] - <NSThread: 0x6000015ad0c0>{number = 7, name = (null)}
发送方-[NSThread currentThread] - <NSThread: 0x600001527b40>{number = 1, name = main}
test-[NSThread currentThread] - <NSThread: 0x600001527b40>{number = 1, name = main}

3. 发送方在子线程,接收方在主线程
发送方
dispatch_async(dispatch_get_global_queue(0, 0), ^{
        NSLog(@"发送方-[NSThread currentThread] - %@", [NSThread currentThread]);
        [[NSNotificationCenter defaultCenter] postNotificationName:@"kNotificationName" object:nil];
    });

监听方
dispatch_async(dispatch_get_main_queue(), ^{
        NSLog(@"接收方-[NSThread currentThread] - %@", [NSThread currentThread]);
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(test) name:@"kNotificationName" object:nil];
    });

- (void)test
{
    NSLog(@"test-[NSThread currentThread] - %@", [NSThread currentThread]);
}

打印结果:
接收方-[NSThread currentThread] - <NSThread: 0x600002c6ba80>{number = 1, name = main}
发送方-[NSThread currentThread] - <NSThread: 0x600002c1a400>{number = 6, name = (null)}
test-[NSThread currentThread] - <NSThread: 0x600002c1a400>{number = 6, name = (null)}

4. 发送方在子线程,接收方在子线程
发送方
dispatch_async(dispatch_get_global_queue(0, 0), ^{
        NSLog(@"发送方-[NSThread currentThread] - %@", [NSThread currentThread]);
        [[NSNotificationCenter defaultCenter] postNotificationName:@"kNotificationName" object:nil];
    });

监听方
dispatch_async(dispatch_get_global_queue(0, 0), ^{
        NSLog(@"接收方-[NSThread currentThread] - %@", [NSThread currentThread]);
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(test) name:@"kNotificationName" object:nil];
    });

- (void)test
{
    NSLog(@"test-[NSThread currentThread] - %@", [NSThread currentThread]);
}

打印结果:
接收方-[NSThread currentThread] - <NSThread: 0x6000039f6940>{number = 6, name = (null)}
发送方-[NSThread currentThread] - <NSThread: 0x600003904a00>{number = 7, name = (null)}
test-[NSThread currentThread] - <NSThread: 0x600003904a00>{number = 7, name = (null)}

以上结果可以验证:
执行的方法所在的线程与 发送通知 的线程有关

在以
- (id <NSObject>)addObserverForName:(nullable NSNotificationName)name object:(nullable id)obj queue:(nullable NSOperationQueue *)queue usingBlock:(void (^)(NSNotification *note))block API_AVAILABLE(macos(10.6), ios(4.0), watchos(2.0), tvos(9.0));
这种方式监听通知的情况下:
如果queue为nil,则block中的代码块执行的线程与发送通知的线程有关
如果queue有值,则与queue队列的属性有关。

接下来,我们验证一下,分别看一下6种情况:
1.发送方在主线程,接收方queue是子队列
2.发送方在子线程,接收方queue为主队列
3.发送方在主线程,接收方queue为nil,
4.发送方在子线程,接收方queue为nil
5.发送方在主线程,接收方queue是主队列
6.发送方在子线程,接收方queue为子队列
明显第5种在主线程,第6种在子线程,因此,不做考虑。

1. 发送方在主线程,接收方queue是子队列
发送方
dispatch_async(dispatch_get_main_queue(), ^{
        NSLog(@"发送方-[NSThread currentThread] - %@", [NSThread currentThread]);
        [[NSNotificationCenter defaultCenter] postNotificationName:@"kNotificationName" object:nil];
    });
    
接收方
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    [[NSNotificationCenter defaultCenter] addObserverForName:@"kNotificationName" object:nil queue:queue usingBlock:^(NSNotification * _Nonnull note) {
        NSLog(@"接收方-[NSThread currentThread] - %@", [NSThread currentThread]);
    }];

打印结果
发送方-[NSThread currentThread] - <NSThread: 0x600003679c00>{number = 1, name = main}
接收方-[NSThread currentThread] - <NSThread: 0x60000362cd40>{number = 5, name = (null)}

2. 发送方在子线程,接收方queue是主队列
发送方
dispatch_async(dispatch_get_global_queue(0, 0), ^{
        NSLog(@"发送方-[NSThread currentThread] - %@", [NSThread currentThread]);
        [[NSNotificationCenter defaultCenter] postNotificationName:@"kNotificationName" object:nil];
    });	
    
接收方
NSOperationQueue *queue = [NSOperationQueue mainQueue];
    [[NSNotificationCenter defaultCenter] addObserverForName:@"kNotificationName" object:nil queue:queue usingBlock:^(NSNotification * _Nonnull note) {
        NSLog(@"接收方-[NSThread currentThread] - %@", [NSThread currentThread]);
    }];

打印结果
发送方-[NSThread currentThread] - <NSThread: 0x600000ebda40>{number = 4, name = (null)}
接收方-[NSThread currentThread] - <NSThread: 0x600000ef50c0>{number = 1, name = main}

3. 发送方在主线程,接收方queue为nil
发送方
dispatch_async(dispatch_get_main_queue(), ^{
        NSLog(@"发送方-[NSThread currentThread] - %@", [NSThread currentThread]);
        [[NSNotificationCenter defaultCenter] postNotificationName:@"kNotificationName" object:nil];
    });	
    
接收方
[[NSNotificationCenter defaultCenter] addObserverForName:@"kNotificationName" object:nil queue:nil usingBlock:^(NSNotification * _Nonnull note) {
        NSLog(@"接收方-[NSThread currentThread] - %@", [NSThread currentThread]);
    }];

打印结果
发送方-[NSThread currentThread] - <NSThread: 0x6000004b1600>{number = 1, name = main}
接收方-[NSThread currentThread] - <NSThread: 0x6000004b1600>{number = 1, name = main}

4. 发送方在子线程,接收方queue为nil
发送方
dispatch_async(dispatch_get_global_queue(0, 0), ^{
        NSLog(@"发送方-[NSThread currentThread] - %@", [NSThread currentThread]);
        [[NSNotificationCenter defaultCenter] postNotificationName:@"kNotificationName" object:nil];
    });	
    
接收方
[[NSNotificationCenter defaultCenter] addObserverForName:@"kNotificationName" object:nil queue:nil usingBlock:^(NSNotification * _Nonnull note) {
        NSLog(@"接收方-[NSThread currentThread] - %@", [NSThread currentThread]);
    }];

打印结果
发送方-[NSThread currentThread] - <NSThread: 0x600001361c00>{number = 3, name = (null)}
接收方-[NSThread currentThread] - <NSThread: 0x600001361c00>{number = 3, name = (null)}

以上结果验证了:
如果queue为nil,则block中的代码块执行的线程与发送通知的线程有关。
如果queue有值,则与queue队列的属性有关。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值