模擬系統(tǒng)KVO實(shí)現(xiàn)

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
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 朋友說(shuō),初雪之時(shí),要和最重要的人在一起。 這座小城的雪姍姍來(lái)遲,在全國(guó)各地飛雪漫天的時(shí)候,小城只是連陰了幾日,飄了...
    素_塵閱讀 1,068評(píng)論 0 1
  • 今晚強(qiáng)哥來(lái)喝酒,很好,我很開心。別看平時(shí)一個(gè)大大咧咧的老好人,其實(shí)心中都有一本賬,有自己的思考。 人成熟不就是這樣...
    20歲少年像木偶閱讀 114評(píng)論 0 0

友情鏈接更多精彩內(nèi)容