KVO 的進(jìn)一步理解

這是一篇簡(jiǎn)單又豐富的簡(jiǎn)書活鹰,周末愉快!

一派哲、KVO概要及簡(jiǎn)單使用

KVO 就是一種監(jiān)聽臼氨,那是如何做到監(jiān)聽的呢?首先創(chuàng)建一個(gè)簡(jiǎn)單的 Class芭届,代碼如下:

#import <Foundation/Foundation.h>

@interface KVOObject : NSObject

// 姓名
@property (nonatomic, copy) NSString* name;

@end



#import "KVOObject.h"

@implementation KVOObject

@end

很簡(jiǎn)單储矩, 就一個(gè) Class感耙,然后定義了一個(gè) name 屬性而已。

一個(gè)簡(jiǎn)單的試驗(yàn)如下:

// 創(chuàng)建一個(gè) KVO 對(duì)象
KVOObject* kvObj = [[KVOObject alloc] init];
kvObj.name = @"HG";

// 添加 KVO 監(jiān)聽
[kvObj addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:NULL];

kvObj.name = @"CoderHG";

// 移除 KVO 監(jiān)聽, 在 iOS 10之前不移除的話直接 crash, 之后的就沒事了
[kvObj removeObserver:self forKeyPath:@"name"];

具體的監(jiān)聽方法代碼如下:

// KVO 的系統(tǒng)監(jiān)聽方法
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
    NSLog(@"%@, %@, %@", keyPath, object, change);
}

以上就是一個(gè)簡(jiǎn)單而完整的 KVO 的使用場(chǎng)景椰苟,但是具體是什么原理呢抑月?

二、KVO 的原理初步認(rèn)識(shí)

都知道一個(gè)對(duì)象一旦添加了 KVO 監(jiān)聽舆蝴,在本質(zhì)上是系統(tǒng)動(dòng)態(tài)的改變了該對(duì)象的 isa 指針谦絮。如果對(duì) isa 不了解的話,可以看這個(gè) OC 小專題洁仗。
想要知道 isa 有什么樣的變動(dòng)层皱,先實(shí)現(xiàn)如下一個(gè)方法:

// 打印具體的 cls 中的方法信息
- (NSString*)printMethodNamesOfClass:(Class)cls {
    unsigned int count;
    // 獲得方法數(shù)組
    Method *methodList = class_copyMethodList(cls, &count);
    
    // 存儲(chǔ)方法名
    NSMutableString *methodNames = [NSMutableString string];
    
    // 遍歷所有的方法
    for (int i = 0; i < count; i++) {
        // 獲得方法
        Method method = methodList[i];
        // 獲得方法名
        NSString *methodName = NSStringFromSelector(method_getName(method));
        // 拼接方法名
        [methodNames appendString:methodName];
        [methodNames appendString:@", "];
    }
    
    // 釋放
    free(methodList);
    
    // 返回類名與方法列表
    return [NSString stringWithFormat:@"類名: %@ \n方法列表: %@", NSStringFromClass(cls), methodNames];
}

然后將以上的試驗(yàn)修改一下,如下:

// 常規(guī)用法
- (void)convention {
    // 創(chuàng)建一個(gè) KVO 對(duì)象
    KVOObject* kvObj = [[KVOObject alloc] init];
    kvObj.name = @"HG";
    
    Class cls = object_getClass(kvObj);
    NSString* isaInfo = [self printMethodNamesOfClass:cls];
    
    NSLog(@"\n\n添加 KVO 之前:\n%@", isaInfo);
    
    
    // 添加 KVO 監(jiān)聽
    [kvObj addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:NULL];
    
    kvObj.name = @"CoderHG";
    
    cls = object_getClass(kvObj);
    isaInfo = [self printMethodNamesOfClass:cls];
    NSLog(@"\n\n添加 KVO 之后:\n%@", isaInfo);
    
    // 移除 KVO 監(jiān)聽, 在 iOS 10之前不移除的話直接 crash, 之后的就沒事了
    [kvObj removeObserver:self forKeyPath:@"name"];
}

日志有如下的打釉省:

添加 KVO 之前:
類名: KVOObject 
方法列表: .cxx_destruct, name, setName:

添加 KVO 之后:
類名: NSKVONotifying_KVOObject 
方法列表: setName:, class, dealloc, _isKVOA

說明在添加 KVO 監(jiān)聽之后叫胖,isa 指針的值確實(shí)是變了,具體變化為:

  • 1她奥、將之前的 KVOObject瓮增, 更換成 NSKVONotifying_KVOObject
  • 2、在 NSKVONotifying_KVOObject 中重寫了 setName:哩俭、class 與 dealloc 方法绷跑,以及添加了一個(gè) _isKVOA 方法。

三凡资、KVO 與 KVC 的那一份藕斷絲連

關(guān)于 KVC砸捏,強(qiáng)烈建議看一下這篇文章KVC 的原理概述,接下來將會(huì)在這篇文章的基礎(chǔ)上做介紹隙赁。如果不看的話垦藏,可能你很難理解我所說的 非常規(guī) KVC 調(diào)用是什么意思。雖然伞访,我在這里僅僅是用到了那么一丁點(diǎn)的內(nèi)容掂骏。
首先創(chuàng)建一個(gè) Class, 代碼如下:

#import <Foundation/Foundation.h>

@interface KVO8KVCObject : NSObject

@end


#import "KVO8KVCObject.h"

@interface KVO8KVCObject ()
{
    // 非常規(guī)試驗(yàn)
    NSString* isGoddess;
}

@end

@implementation KVO8KVCObject

@end

那接下來厚掷,我們想要表達(dá)一個(gè)什么問題呢弟灼?

KVC 能否觸發(fā) KVO 監(jiān)聽?

看了上面的 KVO8KVCObject 定義蝗肪,我即將使用一個(gè) KVC 的非常規(guī)調(diào)用來介紹袜爪,具體代碼如下:

// KVO 與 KVC 那一段藕斷絲連的區(qū)域
- (void)kvo8kvc {
   // 創(chuàng)建一個(gè) KVO8KVC 對(duì)象
   KVO8KVCObject* kvO_CObj = [[KVO8KVCObject alloc] init];
   
   // 通過 KVC 賦值
   [kvO_CObj setValue:@"KJ" forKey:@"goddess"];
   
   Class cls = object_getClass(kvO_CObj);
   NSString* isaInfo = [self printMethodNamesOfClass:cls];
   
   NSLog(@"\n\n添加 KVO 之前:\n%@", isaInfo);
   
   
   // 添加 KVO 監(jiān)聽
   [kvO_CObj addObserver:self forKeyPath:@"goddess" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:NULL];
   
   // 通過 KVC 賦值
   [kvO_CObj setValue:@"JK" forKey:@"goddess"];
   
   cls = object_getClass(kvO_CObj);
   isaInfo = [self printMethodNamesOfClass:cls];
   NSLog(@"\n\n添加 KVO 之后:\n%@", isaInfo);
   
   // 移除 KVO 監(jiān)聽, 在 iOS 10之前不移除的話直接 crash, 之后的就沒事了
   [kvO_CObj removeObserver:self forKeyPath:@"goddess"];
}

看一下具體的 Log 打印蠕趁,如下:

添加 KVO 之前:
類名: KVO8KVCObject 
方法列表: .cxx_destruct

goddess, <KVO8KVCObject: 0x60c00001b080>, {
    kind = 1;
    new = JK;
    old = KJ;
}

添加 KVO 之后:
類名: NSKVONotifying_KVO8KVCObject 
方法列表: class, dealloc, _isKVOA

可以得出結(jié)論:
KVC 能觸發(fā) KVO 監(jiān)聽薛闪。

看到這里,也推翻了之前的一個(gè)結(jié)論:KVO 的正常觸發(fā)的入口是 setter 方法俺陋,其實(shí)不是這樣的豁延,就如同上面的這個(gè)實(shí)驗(yàn)昙篙,在 NSKVONotifying_KVO8KVCObject 與 KVO8KVCObject 中根本就沒有其對(duì)應(yīng)的 setter 方法。

四诱咏、面試題

KVO如何對(duì)集合類進(jìn)行監(jiān)聽?

這個(gè)面試題主要針對(duì)的是一個(gè)集合類中元素變動(dòng)的監(jiān)聽苔可,比如一個(gè)數(shù)組如何如何監(jiān)聽到 添加、插入與刪除袋狞。按照常規(guī)的 KVO 方式焚辅,是監(jiān)聽不到的,但是系統(tǒng)已經(jīng)為我們準(zhǔn)備了專門的 API苟鸯。
具體的介紹同蜻,看一參考 InterviewKVOController 中的具體實(shí)現(xiàn):

/** KVO如何對(duì)集合類進(jìn)行監(jiān)聽?
 1. 需要借助一個(gè) Class (KVObject), 監(jiān)聽這個(gè) Class 實(shí)例對(duì)象中的集合屬性
 2. 實(shí)際監(jiān)聽的是 mutableArrayValueForKey: 返回的集合類. 如同替身.
 */

本篇介紹,到這里就要告一段落了早处。在寫本簡(jiǎn)書的時(shí)候湾蔓,我有一個(gè)試驗(yàn) Demo # OC2Nature,可以作為一個(gè)參考砌梆。具體請(qǐng)看 KVO 目錄默责。
同時(shí)別忘了看看本專題的其它文章 OC 小專題


在之前也寫過關(guān)于 KVO 的簡(jiǎn)書,雖然那時(shí)候理解還不是太深入咸包,但是里面依舊是有新東西的桃序。感興趣的話可以去看看:

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末泛释,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子温算,更是在濱河造成了極大的恐慌怜校,老刑警劉巖,帶你破解...
    沈念sama閱讀 207,113評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件注竿,死亡現(xiàn)場(chǎng)離奇詭異茄茁,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)巩割,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評(píng)論 2 381
  • 文/潘曉璐 我一進(jìn)店門裙顽,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人宣谈,你說我怎么就攤上這事愈犹。” “怎么了?”我有些...
    開封第一講書人閱讀 153,340評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵漩怎,是天一觀的道長(zhǎng)勋颖。 經(jīng)常有香客問我,道長(zhǎng)勋锤,這世上最難降的妖魔是什么饭玲? 我笑而不...
    開封第一講書人閱讀 55,449評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮叁执,結(jié)果婚禮上茄厘,老公的妹妹穿的比我還像新娘。我一直安慰自己谈宛,他們只是感情好蚕断,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,445評(píng)論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著入挣,像睡著了一般亿乳。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上径筏,一...
    開封第一講書人閱讀 49,166評(píng)論 1 284
  • 那天葛假,我揣著相機(jī)與錄音,去河邊找鬼滋恬。 笑死聊训,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的恢氯。 我是一名探鬼主播带斑,決...
    沈念sama閱讀 38,442評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼勋拟!你這毒婦竟也來了勋磕?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,105評(píng)論 0 261
  • 序言:老撾萬榮一對(duì)情侶失蹤敢靡,失蹤者是張志新(化名)和其女友劉穎挂滓,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體啸胧,經(jīng)...
    沈念sama閱讀 43,601評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡赶站,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,066評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了纺念。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片贝椿。...
    茶點(diǎn)故事閱讀 38,161評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖陷谱,靈堂內(nèi)的尸體忽然破棺而出烙博,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 33,792評(píng)論 4 323
  • 正文 年R本政府宣布习勤,位于F島的核電站,受9級(jí)特大地震影響焙格,放射性物質(zhì)發(fā)生泄漏图毕。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,351評(píng)論 3 307
  • 文/蒙蒙 一眷唉、第九天 我趴在偏房一處隱蔽的房頂上張望予颤。 院中可真熱鬧,春花似錦冬阳、人聲如沸蛤虐。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽驳庭。三九已至,卻和暖如春氯窍,著一層夾襖步出監(jiān)牢的瞬間饲常,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評(píng)論 1 261
  • 我被黑心中介騙來泰國(guó)打工狼讨, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留贝淤,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,618評(píng)論 2 355
  • 正文 我出身青樓政供,卻偏偏與公主長(zhǎng)得像播聪,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子布隔,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,916評(píng)論 2 344

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

  • 轉(zhuǎn)至元數(shù)據(jù)結(jié)尾創(chuàng)建: 董瀟偉离陶,最新修改于: 十二月 23, 2016 轉(zhuǎn)至元數(shù)據(jù)起始第一章:isa和Class一....
    40c0490e5268閱讀 1,682評(píng)論 0 9
  • KVC KVC定義 KVC(Key-value coding)鍵值編碼,就是指iOS的開發(fā)中衅檀,可以允許開發(fā)者通過K...
    暮年古稀ZC閱讀 2,128評(píng)論 2 9
  • 1.ios高性能編程 (1).內(nèi)層 最小的內(nèi)層平均值和峰值(2).耗電量 高效的算法和數(shù)據(jù)結(jié)構(gòu)(3).初始化時(shí)...
    歐辰_OSR閱讀 29,321評(píng)論 8 265
  • KVC KVC定義 KVC(Key-value coding)鍵值編碼枕磁,就是指iOS的開發(fā)中,可以允許開發(fā)者通過K...
    jackyshan閱讀 51,820評(píng)論 9 200
  • 上半年有段時(shí)間做了一個(gè)項(xiàng)目术吝,項(xiàng)目中聊天界面用到了音頻播放计济,涉及到進(jìn)度條,當(dāng)時(shí)做android時(shí)候處理的不太好排苍,由于...
    DaZenD閱讀 3,013評(píng)論 0 26