勘误
本文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
下的通知内容都包装成一个数组来进行保存.
实现一个自己的简单通知.
- 定义一个类
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];
到这里,我们就已经实现了最基本的代码了.代码其实很简单,主要是理解这种推理的思维.这里其实考虑的情况很简单,真正的通知实现过程应该更复杂.但一切复杂的需求都是从简单开始的,有了这个开头,后面的自然就更加清晰了.
源码看这里
网友评论