一.建立一個繼承自NSObject的類目GXJKVO,在.h檔案中添加兩個方法
//添加觀察者
- (void)addObserver:(NSObject *)observer forKey:(NSString *)key withBlock:(void(^)(id observed, NSString *key, id oldValue, id newValue))block;
//删除觀察者
- (void)removeObserver:(NSObject *)observer forKey:(NSString *)key;
二.在.m中實作這兩個方法
1.首先聲明兩個唯一的key
NSString *const kClassPrefix = @"GXJKVOClassPrefix";
NSString *const kGXJKVOAssociateKey = @“GXJKVOObserverArrayKey";
2.添加一個新類的延展
@interface ObserveInfo : NSObject
@property (strong, nonatomic) id observer;
@property (copy , nonatomic) NSString * key;
@property (copy , nonatomic) void(^observerBlock) (id observerObject, NSString * key, id oldValue, id newValue);
+(id)instanceWithObserver:(id)observer forKey:(NSString *)key block:(void (^)(id, NSString *, id, id))block;
@end
@implementation ObserveInfo
+(id)instanceWithObserver:(id)observer forKey:(NSString *)key block:(void (^)(id, NSString *, id, id))block
{
ObserveInfo *observerInfo = [[ObserveInfo alloc]init];
if (observerInfo) {
observerInfo.observer = observer;
observerInfo.key = key;
observerInfo.observerBlock = block;
}
return observer;
}
@end
3.實作添加觀察者、删除觀察者方法
//函數 通過key獲得setter方法名
NSString *getSetterName(NSString *key) {
NSString *firstCharacter = [key substringFromIndex:1];
return [NSString stringWithFormat:@"set%@%@",[firstCharacter uppercaseString],[key substringFromIndex:1]];
}
//函數 實作通過setter方法名取出key
NSString *getKey(NSString *setName) {
//去掉冒号
NSString *preName = [setName substringToIndex:setName.length - 1];
//去掉set
NSString * name = [preName substringFromIndex:3];
//獲得首字母
NSString * firstCharacter = [name substringToIndex:1];
//傳回字元串
return [NSString stringWithFormat:@"%@%@",[firstCharacter lowercaseString],[name substringFromIndex:1]];
}
//生成衍生類方法
- (Class)makeKVOClass:(Class)originClass
{
NSString *className = NSStringFromClass(originClass);
NSString *kvoClassName = [NSString stringWithFormat:@"%@%@",kClassPrefix,className];
Class kvoClass = objc_allocateClassPair(object_getClass(self), kvoClassName.UTF8String, 0);
objc_registerClassPair(kvoClass);
return kvoClass;
}
//判斷衍生類是否已經實作目前key所對應的setter方法
- (BOOL)hasSelector:(SEL)aSelector
{
//擷取方法連結清單并周遊
unsigned int methodCount = 0;
//擷取方法連結清單
Method *methodArray = class_copyMethodList(object_getClass(self), &methodCount);
//周遊每個方法名
for (int i = 0; i < methodCount; i++) {
Method m = methodArray[i];
if (method_getName(m) == aSelector) {
free(methodArray);
return YES;
}
}
//否則傳回NO
free(methodArray);
return NO;
}
//gxj_kvoSetter完成兩個功能,1通過向父類發消息完成set本來的指派功能,2并行調用觀察者方法
void gxj_kvoSetter(id objc_self, SEL objc_cmd, id newValue) {
//擷取目前的setter方法名
NSString *setterName = NSStringFromSelector(objc_cmd);
//獲得key
NSString *key = getKey(setterName);
//獲得oldValue
id oldValue = [objc_self valueForKey:key];
//objc_msgSendSuper()函數的參數是父類結構體,需要手動建立
struct objc_super selfSuper = {
.receiver = objc_self,
.super_class = class_getSuperclass(object_getClass(objc_self))
};
objc_msgSendSuper(&selfSuper, objc_cmd,newValue);
//并行回調觀察者block
NSMutableArray *observeArray = objc_getAssociatedObject(objc_self, (__bridge const void *)kGXJKVOAssociateKey);
//周遊數組進行回調
for (ObserveInfo *info in observeArray) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
info.observerBlock(objc_self, key, oldValue, newValue);
});
}
}
//添加觀察者
- (void)addObserver:(NSObject *)observer forKey:(NSString *)key withBlock:(void(^)(id observed, NSString *key, id oldValue, id newValue))block
{
//判斷能否進行KVO,是否存在此key對應的屬性setter方法
NSString *setterName = getSetterName(key);
Method setMethod = class_getInstanceMethod(object_getClass(self), NSSelectorFromString(setterName));
if (!setMethod) {
NSLog(@"無法KVO");
return;
}
//是否已經生成衍生類
NSString *className = NSStringFromClass(object_getClass(self));
Class kvoClass = object_getClass(self);
if (![className hasPrefix:kClassPrefix]) {
//生成目前類的衍生類
kvoClass = [self makeKVOClass:object_getClass(self)];
//生成衍生類後,改變目前的類的類辨別,使self變成kvoClass的執行個體
object_setClass(self, kvoClass);
}
//判斷衍生類是否已經實作目前key所對應的setter方法
if (![self hasSelector:NSSelectorFromString(setterName)]) {
//若未實作setter方法,則添加我們實作的kvoSetter
}
//完成以上,就可以将觀察者添加到關聯數組中
NSMutableArray *observerArray = objc_getAssociatedObject(self, (__bridge const void *)kGXJKVOAssociateKey);
ObserveInfo *observerInfo = [ObserveInfo instanceWithObserver:observer forKey:key block:block];
if (!observerArray) {
observerArray = [NSMutableArray array];
objc_setAssociatedObject(self, (__bridge const void *)(kGXJKVOAssociateKey), observerArray, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
[observerArray addObject:observerInfo];
}
//删除觀察者
- (void)removeObserver:(NSObject *)observer forKey:(NSString *)key
{
NSMutableArray *observerArray = objc_getAssociatedObject(self, (__bridge const void *)kGXJKVOAssociateKey);
for (ObserveInfo * observerInfo in observerArray) {
if (observerInfo.observer == observer && [observerInfo.key isEqualToString:key]) {
[observerArray removeObject:observerInfo];
}
}
}
三.在ViewController中調用添加觀察者方法
//建立一個model,添加一個name屬性
Company *p1 = [Company new];
[p1 addObserver:self forKey:@"name" withBlock:^(id observed, NSString *key, id oldValue, id newValue) {
NSLog(@"object = %@, key = %@, oldValue = %@, newValue = %@",observed,key,oldValue,newValue);
}];
p1.name = @"Baidu";
p1.name = @"Tencent";
p1.name = @“Google";
當p1的屬性name被修改時,會提示被修改。