这几天学习的时候看到了某位程序员自制的手动实现带有block回调函数参数的KVO模式,虽然也跟着做了出来,但是缺陷还有很明显的,无法监听非对象。这个暂且不谈,KVO的实现让我有了实现通知机制的想法,NSNotification想来也就是将接收通知的对象加入某个列表中(我用NSArray存储),然后每个通知对象对应一个回调函数列表,每一次接收新通知时遍历查找,代码放上来:
LXD_NotificationCenter.h
@interface LXD_NotificationCenter : NSObject
+ (LXD_NotificationCenter *)defaultCenter;
- (void)addObserver:(id)observer selector:(SEL)aSelector name:(NSString *)aName object:(id)anObject;
- (void)postNotification:(NSNotification *)notification;
- (void)postNotificationName:(NSString *)aName object:(id)anObject;
- (void)postNotificationName:(NSString *)aName object:(id)anObject userInfo:(NSDictionary *)aUserInfo;
- (void)removeObserver:(id)observer;
- (void)removeObserver:(id)observer name:(NSString *)aName object:(id)anObject;
@end
LXD_NotificationCenter.m
#import "LXD_NotificationCenter.h"
#import "LXD_Notification.h"
#import <objc/message.h>
#import <objc/runtime.h>
static LXD_NotificationCenter * defaultCenter = nil;
static NSString * const kNotificationKey = @"LXDNotificationKey";
#pragma mark -- Store Each Observer's SEL
@interface LXD_Observer : NSObject
@property (nonatomic, assign) SEL aSelector;
@property (nonatomic, weak) NSObject * observer;
@property (nonatomic, strong) LXD_Notification * notification;
@end
@implementation LXD_Observer
- (instancetype)initWithObserver: (NSObject *)anObserver selector: (SEL)aSelector name: (NSString *)aName object: (NSObject *)anObject userInfo: (NSDictionary *)userInfo
{
if (self = [super init]) {
self.notification = [[LXD_Notification alloc] initWithName: aName object: anObject userInfo: userInfo];
_aSelector = aSelector;
_observer = anObserver;
}
return self;
}
@end
#pragma mark -- NotificationCenterMethods
@implementation LXD_NotificationCenter
- (instancetype)init
{
if (defaultCenter == nil) {
defaultCenter = [super init];
}
return defaultCenter;
}
- (instancetype)copy
{
return defaultCenter;
}
+ (instancetype)new
{
return [self defaultCenter];
}
- (void)dealloc
{
}
+ (LXD_NotificationCenter *)defaultCenter
{
static dispatch_once_t once;
dispatch_once(&once, ^{
defaultCenter = [[LXD_NotificationCenter alloc] init];
});
return defaultCenter;
}
- (void)addObserver:(id)observer selector:(SEL)aSelector name:(NSString *)aName object:(id)anObject
{
LXD_Observer * newObserver = [[LXD_Observer alloc] initWithObserver: observer selector: aSelector name: aName object: anObject userInfo: nil];
NSMutableArray * observers = objc_getAssociatedObject(self, (__bridge void *)kNotificationKey);
if (observers == nil) {
observers = [NSMutableArray array];
objc_setAssociatedObject(self, (__bridge void *)kNotificationKey, observers, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
[observers addObject: newObserver];
}
- (void)postNotification:(LXD_Notification *)notification
{
NSMutableArray * observers = objc_getAssociatedObject(self, (__bridge void *)kNotificationKey);
for (int i = 0; i < observers.count; i++) {
LXD_Observer * observer = observers[i];
if ([observer.notification.name isEqualToString: notification.name]) {
observer.notification = nil;
observer.notification = notification;
[self sendMsgWithObserver: observer];
break;
}
}
}
- (BOOL)hasParam: (SEL)aSelector
{
NSString * selectorStr = @(sel_getName(aSelector));
return [selectorStr hasSuffix: @":"];
}
- (void)postNotificationName:(NSString *)aName object:(id)anObject
{
NSMutableArray * observers = objc_getAssociatedObject(self, (__bridge void *)kNotificationKey);
for (int i = 0; i < observers.count; i++) {
LXD_Observer * observer = observers[i];
if ([observer.notification.name isEqualToString: aName]) {
observer.notification.object = anObject;
[self sendMsgWithObserver: observer];
break;
}
}
}
- (void)postNotificationName:(NSString *)aName object:(id)anObject userInfo:(NSDictionary *)aUserInfo
{
NSMutableArray * observers = objc_getAssociatedObject(self, (__bridge void *)kNotificationKey);
for (int i = 0; i < observers.count; i++) {
LXD_Observer * observer = observers[i];
if ([observer.notification.name isEqualToString: aName]) {
observer.notification.object = anObject;
observer.notification.userInfo = aUserInfo;
[self sendMsgWithObserver: observer];
break;
}
}
}
- (void)removeObserver:(id)observer
{
}
- (void)removeObserver:(id)observer name:(NSString *)aName object:(id)anObject
{
}
- (void)sendMsgWithObserver: (LXD_Observer *)observer{
NSLog(@"%s", sel_getName(observer.aSelector));
if ([self hasParam: observer.aSelector]) {
void (*objcMsgSend) (id, SEL, LXD_Notification *) = (void *)objc_msgSend;
objcMsgSend(observer.observer, observer.aSelector, observer.notification);
} else {
void (*objcMsgSend) (id, SEL) = (void *)objc_msgSend;
objcMsgSend(observer.observer, observer.aSelector);
}
}
上面我因为实现的时候恰好有事完成到一半就没有继续了,大家可以参照一下把这代码加上。
LXD_Notification.h
#import <Foundation/Foundation.h>
@interface LXD_Notification : NSObject
@property (nonatomic, copy, readonly) NSString * name;
@property (nonatomic, weak) NSObject * object;
@property (nonatomic, copy) NSDictionary * userInfo;
- (instancetype)init;
- (instancetype)initWithName: (NSString *)name object: (NSObject *)object;
- (instancetype)initWithName: (NSString *)name object: (NSObject *)object userInfo:(NSDictionary *)userInfo;
+ (instancetype)notificationWithName: (NSString *)name object:(id)anObject;
+ (instancetype)notificationWithName:(NSString *)name object:(id)anObject userInfo:(NSDictionary *)aUserInfo;
@end
LXD_Notification.m
#import "LXD_Notification.h"
@implementation LXD_Notification
- (instancetype)init
{
return [self initWithName: @"" object: nil userInfo: nil];
}
- (instancetype)initWithName:(NSString *)name object:(NSObject *)object
{
return [self initWithName: name object: object userInfo: nil];
}
- (instancetype)initWithName: (NSString *)name object: (NSObject *)object userInfo:(NSDictionary *)userInfo
{
if (self = [super init]) {
_name = name;
_object = object;
_userInfo = userInfo;
}
return self;
}
+ (instancetype)notificationWithName: (NSString *)name object:(id)anObject
{
return [[self alloc] initWithName: name object: anObject userInfo: nil];
}
+ (instancetype)notificationWithName:(NSString *)name object:(id)anObject userInfo:(NSDictionary *)aUserInfo
{
return [[self alloc] initWithName: name object: anObject userInfo: aUserInfo];
}
@end