1.主要方法
■添加通知 :cf_ddObserver...
■監(jiān)聽通知 :cf_observerValueForKey...
■刪除通知 :cf_removeObserver...
2.核心工作
■ 修改 isa
■ 添加修改后的Setter方法
■ 添加修改后的class方法
■ 通知外界
NSObject+CFKVO.h
#import <Foundation/Foundation.h>
#import "CFObserverInfo.h"
@interface NSObject (CFKVO)
//添加KVO
- (void)cf_addObserver:(NSObject *)observer forKey:(NSString *)key options:(CFKeyValueObservingOptions)options;
//監(jiān)聽KVO
-(void)cf_observeValueForKey:(NSString *)key ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change;
//刪除KVO
- (void)cf_removeObserver:(NSObject *)observer forKey:(NSString *)key;
@end
NSObject+CFKVO.m
#import "NSObject+CFKVO.h"
#import <objc/message.h>
#import <objc/runtime.h>
static const char KVO_observerArr;
static NSString *CFKVONotifying_=@"CFKVONotifying_";
@implementation NSObject (CFKVO)
/*
*關(guān)鍵工作
1.注冊(cè)類,繼承self
2.修改isa
3.重寫setter方法
4.重寫class方法
5.通知外界
*/
//也可以寫成block形式
- (void)cf_addObserver:(NSObject *)observer forKey:(NSString *)key options:(CFKeyValueObservingOptions)options
{
/*
*1.獲取setter方法名
*/
NSString *setterName = [NSString stringWithFormat:@"set%@:",[key capitalizedString]];
SEL setterSEL = NSSelectorFromString(setterName);
/*
*2.獲取對(duì)應(yīng)setter方法
*/
Method setterMethod = class_getInstanceMethod([self class], setterSEL);
if (!setterMethod) {
NSLog(@"指定key不存在,或者沒(méi)有setter方法!");
return;
}
/*
*3.判斷是否已經(jīng)替換過(guò)isa
*/
Class isaClass = object_getClass(self);
NSString *isaName = NSStringFromClass(isaClass);
if (![isaName hasPrefix:CFKVONotifying_]) {
/*
*4.注冊(cè)新類
*/
NSString *oldClassName = NSStringFromClass([self class]);
NSString *isaClassName = [CFKVONotifying_ stringByAppendingString:oldClassName];
isaClass = NSClassFromString(isaClassName);
if (!isaClass) {
//創(chuàng)建新類
isaClass = objc_allocateClassPair([self class], [isaClassName UTF8String], 0);
//注冊(cè)新類
objc_registerClassPair(isaClass);
}
/*
*5.修改原類的isa
*/
object_setClass(self, isaClass);
}
/*
*6.添加setter方法
此時(shí)[self class]=CFKVONotifying_xxx,或者用isaClass
*/
class_addMethod([self class], setterSEL, (IMP)KVO_setter, method_getTypeEncoding(setterMethod));
/*
*7.添加class方法
*/
SEL classSEL = @selector(class);
Method classMethod = class_getInstanceMethod([self class], classSEL);
class_addMethod([self class], classSEL, (IMP)KVO_class, method_getTypeEncoding(classMethod));
/*
*8.處理觀察者
*/
CFObserverInfo *info = [[CFObserverInfo alloc]initWithObserver:observer withKey:key withOptions:options];
NSMutableArray *observerArr = objc_getAssociatedObject(self, &KVO_observerArr);
if (!observerArr) {
observerArr = [NSMutableArray array];
}
[observerArr addObject:info];
objc_setAssociatedObject(self, &KVO_observerArr, observerArr, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
void KVO_setter(id self, SEL _cmd, id newValue)
{
/*
*(1)獲取key setName:--->name
*/
NSString *setterStr = NSStringFromSelector(_cmd);
//key的首字母小寫:n
NSString *str1 = [[setterStr substringWithRange:NSMakeRange(3, 1)] lowercaseString];
//key的剩余字母:ame
NSString *str2 = [setterStr substringWithRange:NSMakeRange(4, [setterStr rangeOfString:@":"].location-4)];
NSString *key = [NSString stringWithFormat:@"%@%@",str1,str2];
/*
*(2)獲取以前的value值
*/
id oldValue = [self valueForKey:key];
/*
*(3)調(diào)用父類的setter方法
*/
struct objc_super superClass;
superClass.receiver = self;
superClass.super_class = class_getSuperclass(object_getClass(self));
((void (*)(void *,SEL,id))(void *)objc_msgSendSuper)(&superClass,_cmd,newValue);
/*
*(4)通知外界
*/
NSMutableArray *observers = objc_getAssociatedObject(self, &KVO_observerArr);
for (CFObserverInfo *info in observers) {
if ([info.key isEqualToString:key]) {
/*
*(5)封裝回傳消息
CFKeyValueObservingOptionNew|CFKeyValueObservingOptionOld = 11
info.options&CFKeyValueObservingOptionNew =11&01 = 01
info.options&CFKeyValueObservingOptionOld =11&10 = 10
*/ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSMutableDictionary<NSKeyValueChangeKey,id> *change =[NSMutableDictionary dictionaryWithCapacity:2];
if (info.options&CFKeyValueObservingOptionNew) {
[change setObject:newValue forKey:NSKeyValueChangeNewKey];
}
if (info.options&CFKeyValueObservingOptionOld) {
[change setObject:oldValue forKey:NSKeyValueChangeOldKey];
}
((void (*)(id,SEL,id,id,id))(void *)objc_msgSend)(info.observer,@selector(cf_observeValueForKey:ofObject:change:),info.key,self,change);
});
}
}
}
Class KVO_class(id self, SEL _cmd)
{
//獲取isa、在獲取isa的父類
return class_getSuperclass(object_getClass(self));
}
-(void)cf_observeValueForKey:(NSString *)key ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change
{
}
- (void)cf_removeObserver:(NSObject *)observer forKey:(NSString *)key;
{
NSMutableArray *observers = objc_getAssociatedObject(self, &KVO_observerArr);
if (!observers||observers.count<=0) {
return;
}
for (CFObserverInfo *info in observers) {
if ([info.key isEqualToString:key]) {
[observers removeObject:info];
objc_setAssociatedObject(self, &KVO_observerArr, observers, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
break;
}
}
/*
*當(dāng)observers為空時(shí),重新設(shè)置isa
*/
if(observers.count<=0)
{
object_setClass(self, [self class]);
}
}
@end
NSObject+CFKVO.h
#import <Foundation/Foundation.h>
typedef NS_OPTIONS(NSUInteger, CFKeyValueObservingOptions)
{
CFKeyValueObservingOptionNew = 0x01, //01
CFKeyValueObservingOptionOld = 0x02 //10
};
@interface CFObserverInfo : NSObject
//監(jiān)聽者
@property(nonatomic,strong)id observer;
//被監(jiān)聽的key
@property(nonatomic,copy)NSString *key;
//監(jiān)聽策略
@property(nonatomic,assign)CFKeyValueObservingOptions options;
- (instancetype)initWithObserver:(id)observer withKey:(NSString *)key withOptions:(CFKeyValueObservingOptions)options;
@end
NSObject+CFKVO.m
#import "CFObserverInfo.h"
@implementation CFObserverInfo
- (instancetype)initWithObserver:(id)observer withKey:(NSString *)key withOptions:(CFKeyValueObservingOptions)options
{
self = [super init];
if (self) {
self.observer = observer;
self.key = key;
self.options = options;
}
return self;
}
@end
CFPerson.h
#import <Foundation/Foundation.h>
#import "NSObject+CFKVO.h"
@interface CFPerson : NSObject
@property(nonatomic,copy)NSString *name;
@end
CFPerson.m
#ifdef DEBUG
#define NSLog(FORMAT, ...) fprintf(stderr,"%s\n",[[NSString stringWithFormat:FORMAT, ##__VA_ARGS__] UTF8String]);
#else
#define NSLog(...)
#endif
#import "CFPerson.h"
@implementation CFPerson
- (void)cf_observeValueForKey:(NSString *)key ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change
{
NSLog(@"屬性%@改變之前的值為:%@",key,change[NSKeyValueChangeOldKey]);
NSLog(@"屬性%@改變之后的值為:%@",key,change[NSKeyValueChangeNewKey]);
}
main.m
#ifdef DEBUG
#define NSLog(FORMAT, ...) fprintf(stderr,"%s\n",[[NSString stringWithFormat:FORMAT, ##__VA_ARGS__] UTF8String]);
#else
#define NSLog(...)
#endif
#import <Foundation/Foundation.h>
#import "CFRuntimeKit.h"
#import "CFPerson.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
CFPerson *person = [[CFPerson alloc]init];
NSLog(@"變化之前========================");
//1.方法列表
NSLog(@"methodArr:%@",[CFRuntimeKit fetchMethodList:object_getClass(person)]);
//2.class
NSLog(@"class:%@",[person class]);
//3.isa
NSLog(@"isa:%@",object_getClass(person));
//4.setter方法的imp
NSLog(@"setterIMP:%p",[person methodForSelector:@selector(setName:)]);
//5.class方法的imp
NSLog(@"classIMP:%p",[person methodForSelector:@selector(class)]);
person.name = @"lilei";
[person cf_addObserver:person forKey:@"name" options:CFKeyValueObservingOptionNew|CFKeyValueObservingOptionOld];
person.name = @"wanger";
NSLog(@"變化之后========================");
//1.方法列表
NSLog(@"methodArr:%@",[CFRuntimeKit fetchMethodList:object_getClass(person)]);
//2.class
NSLog(@"class:%@",[person class]);
//3.isa
NSLog(@"isa:%@",object_getClass(person));
//4.setter方法的imp
NSLog(@"setterIMP:%p",[person methodForSelector:@selector(setName:)]);
//5.class方法的imp
NSLog(@"classIMP:%p",[person methodForSelector:@selector(class)]);
[person cf_removeObserver:person forKey:@"name"];
NSLog(@"還原之后========================");
//1.方法列表
NSLog(@"methodArr:%@",[CFRuntimeKit fetchMethodList:object_getClass(person)]);
//2.class
NSLog(@"class:%@",[person class]);
//3.isa
NSLog(@"isa:%@",object_getClass(person));
//4.setter方法的imp
NSLog(@"setterIMP:%p",[person methodForSelector:@selector(setName:)]);
//5.class方法的imp
NSLog(@"classIMP:%p",[person methodForSelector:@selector(class)]);
}
return 0;
}
變化之前========================
methodArr:(
{
methodName = "cf_observeValueForKey:ofObject:change:";
methodType = "v40@0:8@16@24@32";
},
{
methodName = ".cxx_destruct";
methodType = "v16@0:8";
},
{
methodName = name;
methodType = "@16@0:8";
},
{
methodName = "setName:";
methodType = "v24@0:8@16";
}
)
class:CFPerson
isa:CFPerson
setterIMP:0x100003180
classIMP:0x7fff6412d4b7
變化之后========================
屬性name改變之前的值為:lilei
屬性name改變之后的值為:wanger
methodArr:(
{
methodName = class;
methodType = "#16@0:8";
},
{
methodName = "setName:";
methodType = "v24@0:8@16";
}
)
class:CFPerson
isa:CFKVONotifying_CFPerson
setterIMP:0x1000036e0
classIMP:0x100003de0
還原之后========================
methodArr:(
{
methodName = "cf_observeValueForKey:ofObject:change:";
methodType = "v40@0:8@16@24@32";
},
{
methodName = ".cxx_destruct";
methodType = "v16@0:8";
},
{
methodName = name;
methodType = "@16@0:8";
},
{
methodName = "setName:";
methodType = "v24@0:8@16";
}
)
class:CFPerson
isa:CFPerson
setterIMP:0x100003180
classIMP:0x7fff6412d4b7
Program ended with exit code: 0