自定義KVO(探索KVO底層本質(zhì))

KVO底層實(shí)現(xiàn)原理

  • KVO是基于runtime機(jī)制實(shí)現(xiàn)的
  • 當(dāng)某個(gè)類的屬性對(duì)象第一次被觀察時(shí),系統(tǒng)就會(huì)在運(yùn)行期動(dòng)態(tài)地創(chuàng)建該類的一個(gè)派生類,在這個(gè)派生類中重寫基類中任何被觀察屬性的setter 方法。派生類在被重寫的setter方法內(nèi)實(shí)現(xiàn)真正的通知機(jī)制
  • 如果原類為Person,那么生成的派生類名為NSKVONotifying_Person
  • 每個(gè)類對(duì)象中都有一個(gè)isa指針指向當(dāng)前類,當(dāng)一個(gè)類對(duì)象的第一次被觀察,那么系統(tǒng)會(huì)偷偷將isa指針指向動(dòng)態(tài)生成的派生類,從而在給被監(jiān)控屬性賦值時(shí)執(zhí)行的是派生類的setter方法
  • 鍵值觀察通知依賴于NSObject 的兩個(gè)方法: willChangeValueForKey: 和 didChangevlueForKey:;在一個(gè)被觀察屬性發(fā)生改變之前, willChangeValueForKey:一定會(huì)被調(diào)用,這就 會(huì)記錄舊的值。而當(dāng)改變發(fā)生后,didChangeValueForKey:會(huì)被調(diào)用,繼而 observeValueForKey:ofObject:change:context: 也會(huì)被調(diào)用。
  • 補(bǔ)充:KVO的這套實(shí)現(xiàn)機(jī)制中蘋果還偷偷重寫了class方法,讓我們誤認(rèn)為還是使用的當(dāng)前類,從而達(dá)到隱藏生成的派生類
原理圖

自定義KVO (NSObject+kvo)

頭文件

#import <Foundation/Foundation.h>

@interface NSObject (kvo)
- (void)LYJ_addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(nullable void *)context;

- (void)LYJ_addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(nullable void *)context block:(void(^)(id object ,NSDictionary *change))block;

@end

體文件

/*
 OC的方法
 1.SEL  目錄
 2.IMP  頁碼
 代碼    內(nèi)容
 
 */

#import "NSObject+kvo.h"
#import <objc/message.h>

@implementation NSObject (kvo)

//自定義的KVO
-(void)LYJ_addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context
{
    //1.創(chuàng)建一個(gè)類
    //Person  LYJKVO_Person
    NSString * oldName = NSStringFromClass([self class]);
    NSString * newName = [@"LYJKVO_" stringByAppendingString:oldName];
    //添加類
    
    //- 1.這個(gè)類繼承誰
    //- 2.類名
    Class MyClass = objc_allocateClassPair([self class], newName.UTF8String, 0);
    //注冊類
    objc_registerClassPair(MyClass);
    
    
    //2.動(dòng)態(tài)修改self的類型!!
    object_setClass(self, MyClass);
    
    //3.重寫setName:  -- 給子類對(duì)象添加setName:方法!!
    class_addMethod(MyClass, @selector(setName:), (IMP)setName, "v@:@");
    
    //4.將觀察者綁定到對(duì)象上面
    objc_setAssociatedObject(self, (__bridge const void *)@"objc", observer, OBJC_ASSOCIATION_ASSIGN);
    
    
}

-(void)LYJ_addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context block:(void (^)(id, NSDictionary *))block
{
    
}

void setName(id self,SEL _cmd,NSString * newName){
    struct objc_super person = {self,class_getSuperclass([self class])};
    //修改name屬性!!
    objc_msgSendSuper(&person, _cmd,newName);
    
    //拿出觀察者!!
    id observer = objc_getAssociatedObject(self, @"objc");
    //調(diào)用observer的
    objc_msgSend(observer, @selector(observeValueForKeyPath:ofObject:change:context:),@"name",self,@{@"name":newName},nil);
}

調(diào)用

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
   
    
    Person * p = [[Person alloc] init];
    
    //添加觀察者!!
//    [p addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew context:nil];
    
    [p LYJ_addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew context:nil];
    
    
//   [p LYJ_addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew context:nil block:^(id object, NSDictionary *change) {
//       
//       
//       //實(shí)現(xiàn)!!
//       
//   }];
//    [p LYJ_addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew context:nil block:^(id object, NSDictionary *change) {
//       //說先!!
//    }];
//
//    [p LYJ_addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew context:nil block:^(id object, NSDictionary *change) {
//        
//    }];
//
//    [p LYJ_addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew context:nil block:^(id object, NSDictionary *change) {
//        
//    }];
//
//    [p LYJ_addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew context:nil block:^(id object, NSDictionary *change) {
//        
//    }];
//
//    [p LYJ_addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew context:nil block:^(id object, NSDictionary *change) {
//        
//    }];

    
    _p = p;
}



- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    static int a = 0;
    a++;
    self.p.name = [NSString stringWithFormat:@"%d",a];
//    NSLog(@"%@",self.p.name);
}


@end

調(diào)用可以看到


這里寫圖片描述

此時(shí)person為LYJKVO_PERSON 為person的子類

?著作權(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),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

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