我们知道,两个对象的传值有两种:正向传值和反向传值。
其中,正向传值可以直接将值赋值过去完成传值动作。
而反向传值,一般有三种方法:代理、block和通知
其中,代理和block都是一对一的传值,而涉及到一对多或者是层次结构比较深的情况下,我们一般使用通知。
通知是以[NSNotificationCenter defaultCenter]单例形式存在。
通知一般需要做三步:
- 监听通知(又被称为注册通知)
- 发送通知
- 移除通知
需要注意的是,监听通知需要在发送通知前执行,不然通知监听不到。
首先我们介绍一下通知的使用过程:
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队列的属性有关。