美文网首页iOS日常须知
[iOS]自己实现一个简单的通知

[iOS]自己实现一个简单的通知

作者: 未来行者 | 来源:发表于2018-03-12 00:08 被阅读5793次

    勘误

    本文demo还有些问题,可以先了解下思路,我会验证之后再做更新.

    最近无意中看了一道面试题:说是如果让你来写一个通知,你会怎么写?
    然后我陷入了深深的沉思:经常使用通知,貌似没有去研究它到底是如何实现的,自己能写一个能实现基本功能的通知么?答案是肯定的.

    通知的原理和特性

    我们通过系统通知的API可以了解到它大概的实现原理.

    NSNotification:这是一个包装通知信息的类,类似一个model,存储了notificationName,object,userInfo等信息.
    NSNotificationCenter:顾名思义~通知中心,就是用来管理通知的接收和发送的类.

    通常我们发送一个通知的操作如下:

     [[NSNotificationCenter defaultCenter] postNotificationName:@"TPNotificationName" object:nil userInfo:(nullable NSDictionary *)aUserInfo];
    

    实际上这句话就注册了一个名字为:TPNotificationName的通知.然后指定接收这个通知传的是nil,表示任何对象都可以接收,同时传入了一个字典参数aUserInfo保存需要传递的信息.紧接着,我们可以在需要响应的地方注册观察者,来监听这个通知.

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

    到这里我们就该想一想了,为啥传入一个通知名后就可以实现通知信息的传递呢?
    根据上述NSNotification的介绍,它保存了一些关键信息:notificationName,object,userInfo,那么我们可以大胆的猜测,当我们通过postNotificationName:object:userInfo方法后,里面肯定是有一个类似model的东西把这些信息进行了存储.而且我们很容易就能想到是一个以postNotificationName为key的字典存储了这些内容.但是我们都知道通知是一对多的,那么它可能有很多个观察者observer,而且每个观察者的行为selector都不一样,所以这里我们可以考虑将同一个postNotificationName下的通知内容都包装成一个数组来进行保存.

    实现一个自己的简单通知.

    1. 定义一个类TPNotification,用来存储notificationName,object,userInfo等信息.这里我们仿照系统的API进行设计.这里另外加了两个参数observer和selector.
    @interface TPNotification : NSObject
    @property(strong,nonatomic) id observer;
    @property(strong,nonatomic) id object;
    @property(strong,nonatomic) NSString *postName;
    @property(assign,nonatomic) SEL selecotr;
    @property(copy,nonatomic) NSDictionary *userInfo;
    
    - (instancetype)initWithName:(NSString *)name object:(nullable id)object userInfo:(nullable NSDictionary *)userInfo;
    + (instancetype)notificationWithName:(NSString *)aName object:(nullable id)anObject;
    + (instancetype)notificationWithName:(NSString *)aName object:(nullable id)anObject userInfo:(nullable NSDictionary *)aUserInfo;
    @end
    

    实现文件:

    @implementation TPNotification
    + (instancetype)notificationWithName:(NSString *)aName object:(id)anObject{
        return [[self alloc] initWithName:aName object:anObject userInfo:nil];
    }
    + (instancetype)notificationWithName:(NSString *)aName object:(id)anObject userInfo:(NSDictionary *)aUserInfo{
        return [[self alloc] initWithName:aName object:anObject userInfo:aUserInfo];
    }
    - (instancetype)initWithName:(NSString *)name object:(id)object userInfo:(NSDictionary *)userInfo{
        if (self = [super init]) {
            self.postName = name;
            self.object = object;
            self.userInfo = userInfo;
        }
        return self;
    }
    @end
    

    这样的话,外部传入的相关信息我们都能保存在TPNotification中.
    2.设计通知中心类TPNotificationCenter,同样仿照系统的API进行设计.

    + (instancetype)defaultCenter;
    - (void)addObserver:(id)observer selector:(SEL)aSelector name:(NSString *)aName object:(nullable id)anObject;
    
    - (void)postNotification:(NSNotification *)notification;
    - (void)postNotificationName:(NSString *)aName object:(nullable id)anObject;
    - (void)postNotificationName:(NSString *)aName object:(nullable id)anObject userInfo:(nullable NSDictionary *)aUserInfo;
    
    - (void)removeObserver:(id)observer;
    - (void)removeObserver:(id)observer name:(NSString *)aName object:(nullable id)anObject;
    
    - (id <NSObject>)addObserverForName:(NSString *)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));
    @end
    

    我们按照顺序来:

    第一步:发送通知.self.observers是一个全局字典用来保存对应NotificationName的通知信息.

    - (void)postNotification:(TPNotification *)notification{
        [self postNotificationName:notification.postName object:notification.object userInfo:notification.userInfo];
    }
    - (void)postNotificationName:(NSString *)aName object:(id)anObject{
        [self postNotificationName:aName object:anObject userInfo:nil];
    }
    - (void)postNotificationName:(NSString *)aName object:(id)anObject userInfo:(nullable NSDictionary *)aUserInfo{
        //如果self.observers不存在对应通知,那么就将这个通知信息加入到self.observers中
        [self.lock lock];
        if (![self.observers objectForKey:aName]) {
            //保存同一个NotificationName下的多个通知信息.
            NSMutableArray *observer = [NSMutableArray array];
            TPNotification *noti = [[TPNotification alloc] init];
            noti.postName = aName;
            noti.object = anObject;
            noti.userInfo = aUserInfo;
            [observer addObject:noti];
            [self.observers setObject:observer forKey:aName];//
        }else{
            //如果存在这个通知那么将当前新的通知信息保存
            NSMutableArray *observer = [self.observers[aName] mutableCopy];
            TPNotification *noti = [[TPNotification alloc] init];
            noti.postName = aName;
            noti.object = anObject;
            noti.userInfo = aUserInfo;
            [observer addObject:noti];
        }   
        [self.lock unlock];
    }
    

    第二步就是注册观察者,进行通知监听了.

    - (void)addObserver:(id)observer selector:(SEL)aSelector name:(NSString *)aName object:(id)anObject{
        //取出当前保存通知信息.
        NSMutableArray *observers = [[self.observers objectForKey:aName] mutableCopy];
        //不存在直接return
        if(!observers) return;
        //遍历信息,并且将传入的observer,aSelector进行保存,目的是为了后面移除观察者.
        //保证线程安全,加锁
        [self.lock lock];
        [observers enumerateObjectsUsingBlock:^(TPNotification *obj, NSUInteger idx, BOOL * _Nonnull stop) {
            obj.observer = observer;
            obj.selecotr = aSelector;
    #pragma clang diagnostic push
    #pragma clang diagnostic ignored "-Warc-performSelector-leaks"
            //对观察者的方法进行响应.
            [observer performSelector:aSelector withObject:obj.userInfo];
    #pragma clang diagnostic pop
        }];
        //重新赋值通知的内容.
        [self.observers setObject:observers forKey:aName];
        [self.lock unlock];
    }
    

    第三步,移除观察者.

    - (void)removeObserver:(id)observer{
        NSArray *keys = [self.observers allKeys];
        [self.lock lock];
        [keys enumerateObjectsUsingBlock:^(id  _Nonnull key, NSUInteger idx, BOOL * _Nonnull stop) {
            NSMutableArray *observers = [self.observers[key] mutableCopy];
            if ([observers containsObject:observer]) {
                [observers removeObject:observer];
                [self.observers setObject:observers forKey:key];
            }
        }];
        [self.lock unlock];
    }
    - (void)removeObserver:(id)observer name:(NSString *)aName object:(id)anObject{
        NSMutableArray *observers = [self.observers[aName] mutableCopy];
        [self.lock lock];
        if ([observers containsObject:observer]) {
            [observers removeObject:observer];
            [self.observers setObject:observers forKey:aName];
        }
        [self.lock unlock];
    }
    

    第四步,然后我们在需要响应的位置调用方法即可.

    [[TPNotificationCenter defaultCenter] addObserver:self selector:@selector(test1) name:@"123" object:nil];
    

    到这里,我们就已经实现了最基本的代码了.代码其实很简单,主要是理解这种推理的思维.这里其实考虑的情况很简单,真正的通知实现过程应该更复杂.但一切复杂的需求都是从简单开始的,有了这个开头,后面的自然就更加清晰了.
    源码看这里

    相关文章

      网友评论

        本文标题:[iOS]自己实现一个简单的通知

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