美文网首页
iOS-Notification 实现原理

iOS-Notification 实现原理

作者: zhouluyao | 来源:发表于2020-04-30 10:13 被阅读0次

通知的构成:

在 iOS 中,NSNotificationNSNotificationCenter是使用观察者模式来实现的用于跨层传递消息。

观察者模式 定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖它的对象都得到通知并自动更新。

首先,信息的传递就依靠通知(NSNotification),也就是说,通知就是信息(执行的方法,观察者本身(self),参数)的包装。

那么这个集合很容易想到就是NSDictionary!key就是namevalue就是NSArray(存放数据模型),里面存放观察者对象。当发送通知时,在通知的字典,根据``name找到value,这个value就是一数组,数组里面存放数据模型(observer、SEL)`。即可执行对应的行为

观察者对象的模型

typedef void(^operationBlock)(CSNotification *notification);
/**
 观察者模型对象
 */
@interface CSObserverModel : NSObject
/** 观察者对象 */
@property(nonatomic, strong)id observer;
/** 执行的方法 */
@property(nonatomic, assign)SEL selector;
/** 通知名字 */
@property(nonatomic, copy)NSString *notificationName;
/** 携带的参数 */
@property(nonatomic, strong)id object;
/** 队列 */
@property(nonatomic, strong)NSOperationQueue *operationQueue;
/** 回调 */
@property(nonatomic, copy)operationBlock block;
@end

添加通知的实现

1、先通过通知名作为key去Map中取key对应的存放注册模型的数组

2、找不到key的话,重新添加一个键值对

3、找到key对应的动态数组,把观察者对象添加到动态数组中

发送通知的实现方式

//发送通知时,在Map中通过key找到数组,然后判断是selector还是block调用,然后调用
- (void)postNotification:(CSNotification *)notification {
    // 根据 name 取出对应观察者数组,执行任务
    NSMutableArray *arrays = (NSMutableArray *)[self.observerJson objectForKey:notification.name];
    
    [arrays enumerateObjectsUsingBlock:^( CSObserverModel *observerModel, NSUInteger idx, BOOL *stop) {
        // 取出数据模型
        id observer = observerModel.observer;
        SEL selector = observerModel.selector;
        
        if (!observerModel.operationQueue) {
            // 下面这样写的目的是:手动忽略clang编译器警告
            // 参考:http://blog.csdn.net/qq_18505715/article/details/76087558
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
            [observer performSelector:selector withObject:notification];
#pragma clang diagnostic pop
        } else {
            // 创建任务
            NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
                // 这里通过 block 回调出去
                observerModel.block(notification);
            }];
            
            // 如果添加观察者 传入 队列,那么任务就放在队列中执行(子线程异步执行)
            [observerModel.operationQueue addOperation:operation];
        }
    }];
}

移除观察者

- (void)removeObserver:(nonnull id)observer name:(nullable NSString *)name object:(nullable id)object {
    // 移除观察者 - 当有 name 参数时
    if (name.length > 0 && [self.observerJson objectForKey:name]) {
        NSMutableArray *arrays = (NSMutableArray *)[self.observerJson objectForKey:name];
        [arrays removeObject:observer];
    } else {
        // 移除观察者 - 当没有 name 参数时
        if (self.observerJson.allKeys.count > 0 && self.observerJson.allValues.count > 0)         {
            NSArray *allKeys = self.observerJson.allKeys;
            
            for (int i = 0; i < allKeys.count; i++) {
                NSMutableArray *keyOfAllObservers = [self.observerJson objectForKey:allKeys[i]];
                
                BOOL isStop = NO;   // 如果找到后就不再遍历后面的数据了
                
                for (int j = 0; j < keyOfAllObservers.count; j++) {
                    // 取出数据模型
                    CSObserverModel *observerModel = keyOfAllObservers[j];
                    
                    if (observerModel.observer == observer) {
                        [keyOfAllObservers removeObject:observerModel];
                        isStop = YES;
                        break;
                    }
                }
                
                if (isStop) {   // 找到了,退出循环
                    break;
                }
            }
        } else {
            NSAssert(false, @"当前通知中心没有观察者");
        }
    }
}

MRC时代,通知中心持有的是注册者的unsafe_unretained指针,在注册者被回收时若不对通知进行手动移除,则指针指向被回收的内存区域,成为野指针。这时再发送通知,便会造成crash。而在iOS 9以后,通知中心持有的是注册者的weak指针,这时即使不对通知进行手动移除,指针也会在注册者被回收后自动置空。

- (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));
//这个API来注册通知,可以直接传入block类型参数。使用这个API会导致注册者被系统retain,因此仍然需要像以前一样手动移除通知,同时这个block类型参数也需注意避免循环引用。

相关文章

网友评论

      本文标题:iOS-Notification 实现原理

      本文链接:https://www.haomeiwen.com/subject/pkzawhtx.html