Runtime-KVO的實(shí)現(xiàn)

實(shí)現(xiàn)分解圖.png

KVO的實(shí)現(xiàn)其實(shí)就是分2步驟
1 讓被監(jiān)聽的對(duì)象isa指向中間類
2 在中間類中重寫setter方法 添加監(jiān)聽方法

首先
左邊是我們很容易理解 每個(gè)對(duì)象都有一個(gè)Class
右邊是在對(duì)象添加KVO之后 runtime的一些實(shí)現(xiàn)

具體代碼實(shí)現(xiàn)
為了方便理解
下面有這樣一個(gè)需求
Person 類 下面有個(gè)對(duì)象 xiaoming(小明) 監(jiān)聽小明改名字
1 創(chuàng)建一個(gè)中間類

// 參數(shù) 1是類名 2是被復(fù)制的類名 3是額外補(bǔ)充的內(nèi)存空間
Class Person_copy = objc_allocateClassPair(Person,@"Person_copy".UTF8String,0);

// 復(fù)制class 方法
Class kvoClass = objc_allocateClassPair(originClass, kvoClassName.UTF8String, 0); 
Method classMethod = class_getClassMethod(Person, @selector(class));
const char *types = method_getTypeEncoding(classMethod);
class_addMethod(Person_copy, @selector(class), (IMP)kvo_class, types);
    
 objc_registerClassPair(Person_copy);
static Class kvo_class(id self, SEL _cmd) {
    return class_getSuperclass(object_getClass(self));
}

這樣我們就擁有了一個(gè)Person_copy的中間類
2 為中間類添加一個(gè)setter 方法
我們知道當(dāng) xiaoming.name = @"xx"的時(shí)候, 會(huì)想上尋找MethodList中是否有setter方法,現(xiàn)在我們還沒有改變isa的指向。先為中間類添加這樣一個(gè)setter方法。
看圖上 setter方法分為2部分
第一部分 消息轉(zhuǎn)發(fā)到原本的Class 處理
第二部分 監(jiān)聽回調(diào)

這邊我一直覺得用objc_msgSend 發(fā)現(xiàn)不對(duì),很尷尬。最后發(fā)現(xiàn)msgSend只能發(fā)發(fā)送instance ,這時(shí)候只能用下面的SendSuper


009F06C0-9209-4177-AF07-A6D91241660B.png
static void kvo_setter(id self, SEL _cmd, id newValue) {
    // 1  看注釋 創(chuàng)建第一個(gè)參數(shù) struct objc_super
    //  這時(shí)候xiaoming的isa 還是指向 Person的
  struct objc_super superClass = {
      .receiver = xiaoming,
      .super_class = class_getSuperclass(object_getClass(xiaoming))
  }
  // 這里有點(diǎn)注意一下直接使用 objc_msgSendSuper的時(shí)候 會(huì)產(chǎn)生 too many arg的警告
  //網(wǎng)上有2種解決辦法 1 改設(shè)置 2創(chuàng)建一個(gè)中轉(zhuǎn)變量讓編譯器懵逼(我們使用這一種)
  void (*objc_msgSendSuperCopy)(void *, SEL , id) = (void *)       
  objc_msgSendSuper;
  objc_msgSendSuperCasted(&superClass,_cmd,newValue);

//**********上面 就是把消息轉(zhuǎn)發(fā)給Person了******下面再添加監(jiān)聽的方法
//監(jiān)聽的方法 可以使用Block  將回調(diào)Block 使用關(guān)聯(lián)到 實(shí)例變量上
//這里就自由發(fā)揮了

}

3 這是我們中間類 算是真正的創(chuàng)建好了 怎么讓屬性賦值的時(shí)候(對(duì)象調(diào)用setter 方法的時(shí)候) 去調(diào)用我們寫好的中間類呢

 //這里只要改變對(duì)象的isa 指向就可以了
object_setClass(xiaoming, Person_copy);

這樣差不多了
這里再補(bǔ)充一下監(jiān)聽回調(diào)的方法

//1使用屬性關(guān)聯(lián) 將處理Block 綁定到實(shí)例變量上
objc_setAssociatedObject(xiaoming, (__bridge const void *)(key), @“這里是處理Block”, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
//2 在Person_copy的setter方法調(diào)用BLOCK回調(diào)
//取出
id 回調(diào) = objc_getAssociatedObject(self, (__bridge const void *)key);
執(zhí)行回調(diào)

一開始覺得KVO實(shí)現(xiàn)很麻煩,理解之后覺得挺簡(jiǎn)單。算是做個(gè)筆記吧。具體代碼,以后再傳吧。。。。。GG

最后編輯于
?著作權(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)容

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