詳解KVO

????對于有iOS開發(fā)經(jīng)驗的同學(xué)來說KVO不在陌生,但是有好多同學(xué)只是停留在會用的基礎(chǔ)上眉撵,但是對于其的深層原理還是不怎么了解侦香,接下來我們來深入的認(rèn)識一下KVO的本質(zhì)。

? ? KVO被我們經(jīng)常稱之為:鍵值監(jiān)聽纽疟,可以用于監(jiān)聽某個對象的屬性值的改變罐韩。接下來我們創(chuàng)建工程先實使用一下:

#import "ViewController.h"

@interface Person :NSObject

@property (assign, nonatomic)int age;

@end

@implementation Person

- (void)setAge:(int)age{? ? _age =age;}

@end

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {

? ? [super viewDidLoad];? ?

????Person *person =[Person new];?

? ?person.age =10;? ?

[person addObserver:self forKeyPath:@"age" options:NSKeyValueObservingOptionNew |NSKeyValueObservingOptionOld context:nil];? ?

person.age =12;}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context{?

? NSLog(@"keyPath :%@----change:%@",keyPath,change);

}

@end

創(chuàng)建了Person對象,一個age屬性污朽,將person對象被添加為controller監(jiān)聽age屬性的變化散吵,打印出來:

2018-04-21 22:33:44.652460+0800 kvo[5397:332335] keyPath :age----change:{? ?

kind = 1;?

new = 12;? ?

old = 10;

}

上邊就是一個KVO使用的全部過程,接下來我們分析:在person的age屬性被改變的時候其實是調(diào)用了它的set方法蟆肆,但是set方法里面又沒有額外的代碼執(zhí)行矾睦,那么它是什么時候?qū)ontroller對象進(jìn)行通知的呢?有基礎(chǔ)的同學(xué)可能就知道那么它肯定是在運(yùn)行時做了一些事情炎功,在runtime的時候?qū)erson對象做了一些東西才會被監(jiān)聽的枚冗。接下來我們對程序打兩個個斷點(diǎn)看:


第一個斷點(diǎn)所看到的person對象信息
第二個斷點(diǎn)所看到person對象信息

那么看到這幅圖,相信了解OC對象實質(zhì)的同學(xué)都已經(jīng)看到了現(xiàn)在person對象的isa的指向在第一個斷點(diǎn)和第二個斷點(diǎn)發(fā)生了變化蛇损,而且person對象的地址也發(fā)生了變化赁温,我靠好神奇啊州藕!是不是覺得背后有一雙手在person對象添加鍵值監(jiān)聽的時候把的person對象給改變了變成了NSKVONotifying_Person類束世,答案是確實這樣!

來分析:在person為被加入監(jiān)聽的時候其實它的調(diào)用時這樣的:

小碼哥_李明杰老師的圖

而被添加監(jiān)聽之后其實是這樣的:

小碼哥_李明杰老師的圖

從上圖和我們的斷點(diǎn)中看出person對象在添加監(jiān)聽之后就衍生出了一個Person的子類NSKVONotifying_Person床玻,它的內(nèi)部信息如圖所示,所以調(diào)用賦值的過程其實是調(diào)用的NSKVONotifying_Person的set方法沉帮,它的內(nèi)部調(diào)用了一個C語言的私有函數(shù)_NSSstIntValueAndNotify()(中間int會根據(jù)你的屬性的類型做改變?nèi)纾篲NSSstCharValueAndNotify()...)這個函數(shù)锈死,但是這個函數(shù)其實內(nèi)部會調(diào)用:

衍生類里面所做的事情(偽代碼)

只有當(dāng)will...和didChange...都調(diào)用的時候就會通知監(jiān)聽者,但是是在didChange...中通知的穆壕,而且如果在controller中直接實現(xiàn)這兩個方法待牵,在屬性值不改變的情況下也會出發(fā)監(jiān)聽回調(diào),可以作為手動出發(fā)監(jiān)聽的回調(diào)喇勋。

接下來我們驗證一下上邊的結(jié)論缨该,查看一下添加監(jiān)聽之后兩個對象的類對象的方法都有什么?

Person *p1 = [[Person alloc] init];? ? ?

p1.age = 1;? ? ?

Person *p2 = [[Person alloc] init];? ? ?

p2.age = 2;? //創(chuàng)建了兩個對象做對比? ?

// self監(jiān)聽p1的age屬性//? ?

NSKeyValueObservingOptions options = NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld;//? ?

[p1 addObserver:self forKeyPath:@"age" options:options context:@"123"];? ?

[self printMethods:object_getClass(p2)];? ?

[self printMethods:object_getClass(p1)];

//該方法是mj老師寫的打印一個類對象里面的方法信息

- (void)printMethods:(Class)cls{? ?

unsigned int count;? ?

Method *methods = class_copyMethodList(cls, &count);?? ?? ?

NSMutableString *methodNames = [NSMutableString string];? ?

[methodNames appendFormat:@"%@ - ", cls];?? ?? ?

for (int i = 0; i < count; i++) {? ? ? ?

Method method = methods[i];?? ? ? ?? ? ? ?

NSString *methodName = NSStringFromSelector(method_getName(method));? ? ? ? [methodNames appendString:methodName];? ? ? ?

[methodNames appendString:@" "];? ? }?? ?? ?

NSLog(@"%@", methodNames);?? ?? ?

free(methods);//C語言的對象需要釋放

}

打印的結(jié)果如下:

Person - setAge: age

NSKVONotifying_Person - setAge: class dealloc _isKVOA

由此可見這是兩個類對象的信息川背,所以被添加監(jiān)聽的person對象調(diào)用set方法時候是調(diào)用的NSKVONotifying_Person里面的方法的贰拿,還有個驗證如圖:

被添加KVO前后方法的地址

由圖看見新建的p1和p2對象蛤袒,p1被添加了KVO監(jiān)聽前后所使用的set方法地址發(fā)生了改變,而p2沒被添加監(jiān)聽所以沒有發(fā)生改變膨更,由此說明p1被添加前后的set調(diào)用確實發(fā)了改變妙真。然后我們程序上打上斷點(diǎn)通過LLDB指令看一下到底發(fā)生了什么改變,如圖:

通過lldb指令查看具體調(diào)用什么執(zhí)行文件

注意:Interview05-KVO是建立的項目名字荚守,是當(dāng)前項目的可執(zhí)行文件珍德,圖是借用的mj老師的

我們可以看出,p1沒被添加監(jiān)聽之前的set是執(zhí)行的目前建立的項目的可執(zhí)行文件矗漾,而被添加之后是執(zhí)行的Foundation的這個可執(zhí)行文件锈候,而且執(zhí)行的是:_NSSetIntValueAndNotify這個函數(shù),所以證明了之前的結(jié)論敞贡,另外還可以通過反編譯oc的Foundation庫查看一下晴及,找下這個函數(shù)不再演示。

最后還有個小說明:為什么person對象被添加監(jiān)聽之后雖然類型改變了但是通過

[person class]嫡锌;

這個方法獲取的類還是Person呢虑稼,這個其實apple做了個掩護(hù),不想讓開發(fā)者知道內(nèi)部做了些什么势木。剩下的咱們可以猜一下蛛倦,其實在NSKOVNotifying_Person這個類中的class的get方法中做了如下操作:

- (Class)class{

????return class_getSuperclass(object_getClass(self));

}

所以person對象在我們添加了監(jiān)聽之后返回的class還是Person這個類,一切都是假象啦啦桌!哈哈

還有在文章中涉及到OC對象本質(zhì)的東西溯壶,可以看我的另外一篇文章:OC的對象本質(zhì)


ps:這個文章其實是我的學(xué)習(xí)筆記了,在小碼哥學(xué)習(xí)李明杰老師的iOS底層網(wǎng)絡(luò)班授課做得筆記甫男,所以用了好多mj老師的東西包括老師的思路和證明方法且改,感謝mj老師在it界的耕耘!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末板驳,一起剝皮案震驚了整個濱河市又跛,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌若治,老刑警劉巖慨蓝,帶你破解...
    沈念sama閱讀 218,755評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異端幼,居然都是意外死亡礼烈,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評論 3 395
  • 文/潘曉璐 我一進(jìn)店門婆跑,熙熙樓的掌柜王于貴愁眉苦臉地迎上來此熬,“玉大人,你說我怎么就攤上這事∠溃” “怎么了募谎?”我有些...
    開封第一講書人閱讀 165,138評論 0 355
  • 文/不壞的土叔 我叫張陵,是天一觀的道長峡碉。 經(jīng)常有香客問我近哟,道長,這世上最難降的妖魔是什么鲫寄? 我笑而不...
    開封第一講書人閱讀 58,791評論 1 295
  • 正文 為了忘掉前任吉执,我火速辦了婚禮,結(jié)果婚禮上地来,老公的妹妹穿的比我還像新娘戳玫。我一直安慰自己,他們只是感情好未斑,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,794評論 6 392
  • 文/花漫 我一把揭開白布咕宿。 她就那樣靜靜地躺著,像睡著了一般蜡秽。 火紅的嫁衣襯著肌膚如雪府阀。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,631評論 1 305
  • 那天芽突,我揣著相機(jī)與錄音试浙,去河邊找鬼。 笑死寞蚌,一個胖子當(dāng)著我的面吹牛田巴,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播挟秤,決...
    沈念sama閱讀 40,362評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼壹哺,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了艘刚?” 一聲冷哼從身側(cè)響起管宵,我...
    開封第一講書人閱讀 39,264評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎昔脯,沒想到半個月后啄糙,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,724評論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡云稚,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了沈堡。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片静陈。...
    茶點(diǎn)故事閱讀 40,040評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出鲸拥,到底是詐尸還是另有隱情拐格,我是刑警寧澤,帶...
    沈念sama閱讀 35,742評論 5 346
  • 正文 年R本政府宣布刑赶,位于F島的核電站捏浊,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏撞叨。R本人自食惡果不足惜金踪,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,364評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望牵敷。 院中可真熱鬧胡岔,春花似錦、人聲如沸枷餐。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽毛肋。三九已至怨咪,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間润匙,已是汗流浹背诗眨。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留趁桃,地道東北人辽话。 一個月前我還...
    沈念sama閱讀 48,247評論 3 371
  • 正文 我出身青樓,卻偏偏與公主長得像卫病,于是被迫代替她去往敵國和親油啤。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,979評論 2 355

推薦閱讀更多精彩內(nèi)容

  • 前言 KVO: Key-Value-Observer,它來源于觀察者模式, 其基本思想(copy于某度)是一個目標(biāo)...
    Monkey_ALin閱讀 3,171評論 3 28
  • 轉(zhuǎn)自小菜鳥dxb => 詳解KVO,KVC 1蟀苛、KVC益咬,即是指 NSKeyValueCoding,一個非正式的Pr...
    小餅干是只松鼠閱讀 399評論 0 2
  • 20- 枚舉,枚舉原始值,枚舉相關(guān)值,switch提取枚舉關(guān)聯(lián)值 Swift枚舉: Swift中的枚舉比OC中的枚...
    iOS_恒仔閱讀 2,280評論 1 6
  • 立秋帜平,民間素有‘’貼秋膘‘’一說幽告。民間流行在立秋這天以懸稱稱人,將體重與立夏時對應(yīng)來檢驗肥瘦裆甩,體重減輕叫‘’苦夏‘...
    春華秋實bj閱讀 302評論 0 1
  • 我開始懷疑冗锁,我有沒有真的喜歡過你,還是在給自己一個答案嗤栓。我只知道那時候整夜整夜的輾轉(zhuǎn)反側(cè)是真的冻河,無盡的心酸是真的箍邮,...
    王卡洛夫閱讀 150評論 0 0