NSNotification消息通知實現源碼(觀察者模式原理)

先簡單介紹蘋果封裝的消息通知,再獻上根據觀察者模式原理實現的源碼供參考。 ios

消息通知

對於觀察者模式,蘋果封裝了消息通知(NSNotification)和通知中心(NSNotificationCenter)供開發者使用。使用也比較簡便,例如:git

// defaultCenter
[[NSNotificationCenter defaultCenter] addObserver:observer selector:@selector(nsSelector:) name:ZZNotificationWithDefaultCenter object:nil];
[[NSNotificationCenter defaultCenter] postNotificationName:ZZNotificationWithDefaultCenter object:@"默認實例 >>>> defaultCenter"];
複製代碼

其中@property (class, readonly, strong) NSNotificationCenter *defaultCenter;只是蘋果提供的一個單例,你也能夠本身建立一個,例如:github

// newCenter
NSNotificationCenter *newCenter = [NSNotificationCenter new];
[newCenter addObserver:observer selector:@selector(nsSelector:) name:ZZNotificationWithNew1 object:nil];
[newCenter postNotificationName:ZZNotificationWithNew1 object:@"newCenter1"];

複製代碼

須要注意的是,addObserver和postNotificationName的必須是同一個NSNotificationCenter實例macos

若是不想調用selector,蘋果也提供了執行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));
複製代碼

原理實現

參照着蘋果提供的接口文檔和使用,以及加上對觀察者模式的理解,試着去實現ZZNotification、ZZNotification (ZZNotificationCreation)、ZZNotificationCenter三個類,前綴NS改爲ZZ,代碼以下:bash

ZZNotification.hpost

typedef NSString *ZZNotificationName;

@interface ZZNotification : NSObject<NSCopying>

@property (readonly, copy) ZZNotificationName name;
@property (nullable, readonly, retain) id object;
@property (nullable, readonly, copy) NSDictionary *userInfo;

- (instancetype)initWithName:(ZZNotificationName)name object:(nullable id)object userInfo:(nullable NSDictionary *)userInfo;

@end
複製代碼

ZZNotification.mui

@implementation ZZNotification

- (instancetype)initWithName:(ZZNotificationName)name object:(id)object userInfo:(NSDictionary *)userInfo
{
    if (self = [super init]) {
        _name = name;
        _object = object;
        _userInfo = userInfo;
    }
    return self;
}

- (nonnull id)copyWithZone:(nullable NSZone *)zone
{
    return self;
}

@end
複製代碼

ZZNotification (ZZNotificationCreation).hatom

@interface ZZNotification (ZZNotificationCreation)

+ (instancetype)notificationWithName:(ZZNotificationName)aName object:(nullable id)anObject;
+ (instancetype)notificationWithName:(ZZNotificationName)aName object:(nullable id)anObject userInfo:(nullable NSDictionary *)aUserInfo;

+ (instancetype)new __attribute__((unavailable("use -initWithName:object:userInfo instead")));
- (instancetype)init __attribute__((unavailable("use -initWithName:object:userInfo instead")));

@end
複製代碼

ZZNotification (ZZNotificationCreation).mspa

@implementation ZZNotification (ZZNotificationCreation)

+ (instancetype)notificationWithName:(ZZNotificationName)aName object:(id)anObject
{
    return [self notificationWithName:aName object:anObject userInfo:nil];
}

+ (instancetype)notificationWithName:(ZZNotificationName)aName object:(id)anObject userInfo:(NSDictionary *)aUserInfo
{
    ZZNotification *notification = [[[self class] alloc] initWithName:aName object:anObject userInfo:aUserInfo];
    return notification;
}

@end
複製代碼

另外,先增長一個管理觀察者屬性的ZZObserverModel:

@interface ZZObserverModel : NSObject

@property (nullable, nonatomic, copy) ZZNotificationName name;  // 通知名稱
@property (nullable, nonatomic, strong) id observer;            // 觀察者
@property (nullable, nonatomic, strong) id object;              // 監聽到通知後傳入對象
@property (nonatomic, assign) SEL selector;                     // 監聽到通知後執行方法
@property (nonatomic, strong) NSOperationQueue *queue;
@property (nonatomic, copy) ZZNotificationBlock block;

@end

@implementation ZZObserverModel

+ (instancetype)modelWithObserver:(id)observer name:(ZZNotificationName)name selector:(SEL)selector object:(id)object
{
    return [self modelWithObserver:observer name:name selector:selector object:object queue:nil block:nil];
}

+ (instancetype)modelWithObserver:(id)observer name:(ZZNotificationName)name selector:(SEL)selector object:(id)object queue:(NSOperationQueue *)queue block:(ZZNotificationBlock)block
{
    ZZObserverModel *model = [[[self class] alloc] init];
    model.observer = observer;
    model.name = name;
    model.selector = selector;
    model.object = object;
    model.queue = queue;
    model.block = block;
    return model;
}

@end
複製代碼

最後實現最重要的ZZNotificationCenter

ZZNotificationCenter.h

@interface ZZNotificationCenter : NSObject

@property (class, readonly, strong) ZZNotificationCenter *defaultCenter;

- (void)addObserver:(id)observer selector:(SEL)aSelector name:(nullable ZZNotificationName)aName object:(nullable id)anObject;

- (void)postNotification:(ZZNotification *)notification;
- (void)postNotificationName:(ZZNotificationName)aName object:(nullable id)anObject;
- (void)postNotificationName:(ZZNotificationName)aName object:(nullable id)anObject userInfo:(nullable NSDictionary *)aUserInfo;

- (void)removeObserver:(id)observer;
- (void)removeObserver:(id)observer name:(nullable ZZNotificationName)aName object:(nullable id)anObject;

- (id <NSObject>)addObserverForName:(nullable ZZNotificationName)name object:(nullable id)obj queue:(nullable NSOperationQueue *)queue usingBlock:(void (^)(ZZNotification *note))block;

@end
複製代碼

ZZNotificationCenter ()

@interface ZZNotificationCenter ()

// 觀察者字典:key是ZZNotificationName,value是NSMutableArray,即全部具備相同名稱的觀察者
@property (nullable, nonatomic, strong) NSMutableDictionary <NSString*, NSMutableArray<ZZObserverModel *>*>*observersDictionary;

@end
複製代碼

ZZNotificationCenter.m

  • 實現單例defaultCenter
// 編譯器不實現getter,需手動實現
@dynamic defaultCenter;

- (instancetype)init
{
    if (self = [super init]) {
        _observersDictionary = [NSMutableDictionary new];
    }
    return self;
}

// 提供單例
+ (ZZNotificationCenter *)defaultCenter
{
    static ZZNotificationCenter *sharedInstance;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        sharedInstance = [[ZZNotificationCenter alloc] init];
    });
    return sharedInstance;
}
複製代碼
  • 增長觀察者
#pragma mark - addObserver

// 增長觀察者,方式:selector
- (void)addObserver:(id)observer selector:(SEL)aSelector name:(ZZNotificationName)aName object:(id)anObject
{
    if (aName) {
        ZZObserverModel *model = [ZZObserverModel modelWithObserver:observer name:aName selector:aSelector object:anObject];
        NSMutableArray *observers = self.observersDictionary[aName];
        if (!observers) {
            observers = [NSMutableArray new];
        }
        [observers addObject:model];
        self.observersDictionary[aName] = observers;
    }
}

// 增長觀察者,方式:queue、block
- (id<NSObject>)addObserverForName:(ZZNotificationName)name object:(id)obj queue:(NSOperationQueue *)queue usingBlock:(void (^)(ZZNotification * _Nonnull))block
{
    if (name) {
        ZZObserverModel *model = [ZZObserverModel modelWithObserver:nil name:name selector:nil object:obj queue:queue block:block];
        NSMutableArray *observers = self.observersDictionary[name];
        if (!observers) {
            observers = [NSMutableArray new];
        }
        [observers addObject:model];
        self.observersDictionary[name] = observers;
    }
    return nil;
}
複製代碼
  • 發送通知
#pragma mark - postNotification

// 發送通知
- (void)postNotification:(ZZNotification *)notification
{
    if (notification.name) {
        NSArray <ZZObserverModel *>*observers = self.observersDictionary[notification.name];
        [observers enumerateObjectsUsingBlock:^(ZZObserverModel * _Nonnull model, NSUInteger idx, BOOL * _Nonnull stop) {
            if (model.queue) {
                NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
                    model.block(notification);
                }];
                NSOperationQueue *queue = model.queue;
                [queue addOperation:operation];
            } else {
                id observer = model.observer;
                SEL selector = model.selector;
                if (observer && [observer respondsToSelector:selector]) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
                    [observer performSelector:selector withObject:notification];
#pragma clang diagnostic pop
                }
            }
/*
            // 或者用消息轉發
            NSMethodSignature *methodSignature = [NSMethodSignature signatureWithObjCTypes:"v@:@"];
            NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSignature];
            SEL sel = observer.selector;
            if ([observer.observer respondsToSelector:sel]) {
                [invocation setTarget:observer.observer];
                [invocation setSelector:sel];
                [invocation setArgument:&notification atIndex:2];
                [invocation invoke];
            }
 */
        }];
    }
}

- (void)postNotificationName:(ZZNotificationName)aName object:(id)anObject
{
    [self postNotification:[ZZNotification notificationWithName:aName object:anObject]];
}

- (void)postNotificationName:(ZZNotificationName)aName object:(id)anObject userInfo:(NSDictionary *)aUserInfo
{
    [self postNotification:[ZZNotification notificationWithName:aName object:anObject userInfo:aUserInfo]];
}
複製代碼
  • 移除觀察者
#pragma mark - removeObserver

// 移除觀察者
- (void)removeObserver:(id)observer
{
    [self removeObserver:observer name:nil object:nil];
}

- (void)removeObserver:(id)observer name:(ZZNotificationName)aName object:(id)anObject
{
    [[self.observersDictionary allValues] enumerateObjectsUsingBlock:^(NSMutableArray<ZZObserverModel *> * _Nonnull observers, NSUInteger idx, BOOL * _Nonnull stop) {
        [observers enumerateObjectsUsingBlock:^(ZZObserverModel * _Nonnull anObserver, NSUInteger idx, BOOL * _Nonnull stop) {
            if (observer && observer == anObserver.observer) {
                if (!aName || [anObserver.name isEqualToString:aName]) {
                    if (!anObject || anObserver.object == anObject) {
                        [observers removeObject:anObserver];
                    }
                }
            }
        }];
    }];
}

@end
複製代碼

Demo參考

23種設計模式代碼ZZDesignPatterns

相關文章
相關標籤/搜索