
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

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