iOS面试总结Objective-CiOS

NSNotification详解

2019-04-23  本文已影响2人  码小菜

目录
一,基本概念
二,NSNotification
三,NSNotificationCenter
四,NSNotificationQueue
五,多线程
六,同步与异步
七,实现原理
八,流程分析

一,基本概念

二,NSNotification

// 通知的名称
@property (readonly, copy) NSNotificationName name;
// 发出通知的对象
@property (nullable, readonly, retain) id object;
// 给观察者传递的数据
@property (nullable, readonly, copy) NSDictionary *userInfo;

三,NSNotificationCenter

// 全局只有一个
@property (class, readonly, strong) NSNotificationCenter *defaultCenter;
// 可以指定收到通知的回调在哪个队列中执行
- (id <NSObject>)addObserverForName:(nullable NSNotificationName)name object:(nullable id)obj queue:(nullable NSOperationQueue *)queue usingBlock:(void (^)(NSNotification *note))block;

答:
1,在iOS9之前需要,iOS9开始不需要
2,iOS9之前通知中心对观察者是unsafe_unretained引用,当观察者释放后,unsafe_unretained指针不会被置为nil,unsafe_unretained就变成了野指针,如果再次收到通知会引起野指针crash
3,iOS9开始改成了weakweak会自动被置为nil,不会出现野指针crash

四,NSNotificationQueue

// 当前线程的默认队列
@property (class, readonly, strong) NSNotificationQueue *defaultQueue;

1,控制通知何时发出

typedef NS_ENUM(NSUInteger, NSPostingStyle) {
    NSPostWhenIdle = 1, // 在当前runloop空闲时发出
    NSPostASAP = 2, // as soon as possible,尽快发出
    NSPostNow = 3 // 通知合并后立即发出
};

2,控制通知如何合并

// 相同的name或者object只会发出一个
typedef NS_OPTIONS(NSUInteger, NSNotificationCoalescing) {
    NSNotificationNoCoalescing = 0, // 不合并
    NSNotificationCoalescingOnName = 1, // 按照name合并
    NSNotificationCoalescingOnSender = 2 // 按照object合并
};
// 添加观察者
[[NSNotificationCenter defaultCenter] addObserverForName:@"NotificationName"
                                                  object:nil
                                                   queue:NSOperationQueue.mainQueue
                                              usingBlock:^(NSNotification * _Nonnull note) {
                                                  // 收到通知的回调
                                                  NSLog(@"receivedNotification");
                                              }];
// 创建通知
NSNotification *notification = [[NSNotification alloc] initWithName:@"NotificationName"
                                                             object:nil
                                                           userInfo:nil];
// 添加通知到队列中
[[NSNotificationQueue defaultQueue] enqueueNotification:notification
                                           postingStyle:NSPostWhenIdle
                                           coalesceMask:NSNotificationNoCoalescing // 不合并
                                               forModes:nil];
[[NSNotificationQueue defaultQueue] enqueueNotification:notification
                                           postingStyle:NSPostWhenIdle
                                           coalesceMask:NSNotificationNoCoalescing // 不合并
                                               forModes:nil];

// 打印结果
receivedNotification
receivedNotification

// 将“不合并”改为“按name合并”

// 打印结果
receivedNotification

3,设置mode:当前线程runloop在指定的mode下,NSNotificationQueue才能将NSNotification发送给NSNotificationCenter

五,多线程

[[NSNotificationCenter defaultCenter] addObserverForName:@"NotificationName"
                                                  object:nil
                                                   queue:nil
                                              usingBlock:^(NSNotification * _Nonnull note) {
                                                  // 收到通知的线程
                                                  NSLog(@"received---%@", NSThread.currentThread);
                                              }];

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    // 发出通知的线程
    NSLog(@"post---%@", NSThread.currentThread);
    [[NSNotificationCenter defaultCenter] postNotificationName:@"NotificationName"
                                                        object:nil
                                                      userInfo:nil];
});

// 打印结果
post---<NSThread: 0x60000126f680>{number = 3, name = (null)}
received---<NSThread: 0x60000126f680>{number = 3, name = (null)}
// 方法一:在回调中强制切换到主线程
dispatch_async(dispatch_get_main_queue(), ^{
     // 更新UI
});

// 方法二:设置回调在主队列中执行
[[NSNotificationCenter defaultCenter] addObserverForName:@"NotificationName"
                                                  object:nil
                                                   queue:NSOperationQueue.mainQueue
                                              usingBlock:^(NSNotification * _Nonnull note) {
                                                   // 更新UI
                                              }];

六,同步与异步

[[NSNotificationCenter defaultCenter] addObserverForName:@"NotificationName"
                                                  object:nil
                                                   queue:nil
                                              usingBlock:^(NSNotification * _Nonnull note) {
                                                  sleep(3);
                                                  NSLog(@"222");
                                              }];

[[NSNotificationCenter defaultCenter] postNotificationName:@"NotificationName"
                                                    object:nil
                                                  userInfo:nil];
NSLog(@"111");

// 打印结果
222
111
[[NSNotificationCenter defaultCenter] addObserverForName:@"NotificationName"
                                                  object:nil
                                                   queue:nil
                                              usingBlock:^(NSNotification * _Nonnull note) {
                                                  sleep(3);
                                                  NSLog(@"222");
                                              }];

NSNotification *notification = [[NSNotification alloc] initWithName:@"NotificationName"
                                                             object:nil
                                                           userInfo:nil];
[[NSNotificationQueue defaultQueue] enqueueNotification:notification
                                           postingStyle:NSPostWhenIdle];
NSLog(@"111");

// 打印结果
111
222

七,实现原理

// 保存name,object与观察者的对应关系
typedef struct NCTable {
  MapTable named; // 保存有传入name的观察者
  MapTable nameless; // 保存没有传入name但有传入object的观察者
  struct Observation *wildcard; // 保存name和object都没有传入的观察者
};
// 保存观察者信息
typedef struct Observation {
  id observer; // 保存添加观察者时传入的observer
  SEL selector; // 保存添加观察者时传入的selector
  struct Observation *next; // 保存监听同一个通知的下一个观察者
};

// 所有添加观察者的方法最后都会调用addObserver:selector:name:object:方法
Named MapTable.png Nameless MapTable.png Wildcard链表.png

八,流程分析

以有传入name和object为例,其他过程类似

1,在初始化NSNotificationCenter时创建一个NCTable
2,根据addObserver方法传入的参数实例化一个Observation
3,根据是否传入name和object选择添加在named MapTablenameless MapTable还是在wildcard链表
4,以name为key在大MapTable中查找对应的value(小MapTable),如果没有就创建新的小MapTable并添加到大MapTable
5,以object为key在小MapTable中查找对应的value(链表),如果找到就把Observation插到链表末尾,如果没有找到就先创建一个头结点然后再插入

1,postNotification方法内部实例化一个NSNotification对象来保存传入的各种参数
2,创建一个数组ObservationArray
3,遍历wildcard链表,把所有observation都添加到ObservationArray中(wildcard链表中的观察者会监听所有的通知)
4,以name和object为key查找对应的链表,也将其所有的observation都添加到ObservationArray中
5,遍历ObservationArray,让每个observation都执行如下的代码

// 让observer调用selector并传入notification
[observation->observer performSelector: observation->selector withObject: notification];

1,根据name和object找到观察者所在的链表
2,根据observer在链表中找到对应的observation并删除

上一篇 下一篇

猜你喜欢

热点阅读