iOS開發(fā) KVO探尋

目錄
1.什么是KVO?
2.KVO實現(xiàn)原理
3.通過KVC設置value值KVO能否生效?
4.通過成員變量直接賦值KVO能否生效?

1.什么是KVO

KVO (Key-Value Observing)是 Objective-C對觀察者設計模式的一種實現(xiàn),
KVO 提供一種機制,指定一個被觀察對象(例如 A 類)邑遏,當對象某個屬性發(fā)生更改時,觀察對象會獲得通知恰矩,并做一些業(yè)務處理.

2.KVO實現(xiàn)原理

Apple官方文檔關于KVO的說明:

Automatic key-value observing is implemented using a technique called isa-swizzling... When an observer is registered for an attribute of an object the isa pointer of the observed object is modified, pointing to an intermediate class rather than at the true class ...

大意就是:KVO是使用isa混寫技術(isa-swizzling)實現(xiàn)的记盒,被觀察對象的 isa 指針會指向一個中間類,而不是原來真正的類.

簡單概述 KVO 的實現(xiàn)

1.當我們觀察一個對象A時外傅,KVO機制會動態(tài)創(chuàng)建一個新的類NSKVONotifying_A.

2.在這個過程纪吮,被觀察對象的 isa 指針從指向原來的 A 類,被 KVO 機制修改為指向系統(tǒng)新創(chuàng)建的子類 NSKVONotifying_A 類萎胰,來實現(xiàn)當前類屬性值改變的監(jiān)聽.

3.NSKVONotifying_A 類重寫了被觀察屬性的 setter 方法碾盟,重寫的 setter 方法會負責在調用原 setter 方法之前和之后,通知所有觀察對象值的更改技竟。

4.這個中間類冰肴,繼承自原本的那個類。

KVO原理演示

代碼演示
"OYObject.h"文件

#import <Foundation/Foundation.h>

@interface OYObject : NSObject
@property (nonatomic, assign) int value;
- (void)TestChange;
@end

"OYObject.m"文件

#import "OYObject.h"

@implementation OYObject
- (instancetype)init
{
    self = [super init];
    if (self) {
        _value = 0;
    }
    return self;
}

- (void)TestChange{
    _value = 5;//直接為成員變量賦值
}
@end

"OYObserver.h"/"OYObserver.m"文件

//OYObserver.h文件
#import <Foundation/Foundation.h>

@interface OYObserver : NSObject

@end

//OYObserver.m文件
#import "OYObserver.h"
#import "OYObject.h"

@implementation OYObserver

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{
    if ([object isKindOfClass:[OYObject class]] && [keyPath isEqualToString:@"value"]) {
        //獲取value新值
        NSNumber *valueNum = [change valueForKey:NSKeyValueChangeNewKey];
        NSLog(@"value 新值:%@",valueNum);
    }
}

@end

ViewController中:

#import "ViewController.h"
#import "OYObject.h"
#import "OYObserver.h"
@interface ViewController ()
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    OYObject *obj = [[OYObject alloc] init];
    OYObserver *observer = [[OYObserver alloc] init];
    //調用kvo方法監(jiān)聽obj的value屬性的變化
    [obj addObserver:observer forKeyPath:@"value" options:NSKeyValueObservingOptionNew context:nil];
    //通過setter方法修改value
    obj.value = 1;
}
@end

設置如下圖兩個斷點
運行后,在LLDB中榔组,po命令:po object_getClassName(obj) 查看此時obj所屬的類,如下圖熙尉,此時obj是OYObject類的實例對象.

往下執(zhí)行一步:po命令:po object_getClassName(obj) 發(fā)現(xiàn)此時obj已經變?yōu)镹SKVONotifying_OYObject的實例對象了,這就驗證了上面所說的KVO會動態(tài)創(chuàng)建一個中間類NSKVONotifying_OYObject搓扯,并且obj的isa指針指向了它

3.通過KVC設置value值KVO能否生效检痰?

#import "ViewController.h"
#import "OYObject.h"
#import "OYObserver.h"
@interface ViewController ()
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    OYObject *obj = [[OYObject alloc] init];
    OYObserver *observer = [[OYObserver alloc] init];
    //調用kvo方法監(jiān)聽obj的value屬性的變化
    [obj addObserver:observer forKeyPath:@"value" options:NSKeyValueObservingOptionNew context:nil];
    //通過setter方法修改value
    obj.value = 1;
    //通過kvc設置value能否生效
    [obj setValue:@2 forKey:@"value"];
}
//不要忘了在dealloc中移除observer,這里就不寫了
@end

輸出:

Runtime-study[17800:14302974] value 新值:1
Runtime-study[17800:14302974] value 新值:2

為什么通過kvc設置value可以讓KVO生效锨推?
因為KVC 會調用obj的setter方法

4.通過成員變量直接賦值KVO能否生效铅歼?

ViewController中調用 給成員變量_value賦值的TestChange方法:

#import "ViewController.h"
#import "OYObject.h"
#import "OYObserver.h"
@interface ViewController ()
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    OYObject *obj = [[OYObject alloc] init];
    OYObserver *observer = [[OYObserver alloc] init];
    //調用kvo方法監(jiān)聽obj的value屬性的變化
    [obj addObserver:observer forKeyPath:@"value" options:NSKeyValueObservingOptionNew context:nil];
    //通過setter方法修改value
    obj.value = 1;

    //通過成員變量直接賦值value能否生效?
    [obj TestChange];
}
//不要忘了在dealloc中移除observer爱态,這里就不寫了
@end

輸出:

Runtime-study[17800:14302974] value 新值:1

通過調用 直接給成員變量_value賦值的TestChange方法谭贪,發(fā)現(xiàn)KVO并沒有生效.
所以直接修改屬性對應的成員變量境钟,是不會觸發(fā)KVO機制的.

再來看下面的代碼,修改 直接給成員變量賦值的方法:

#import "OYObject.h"

@implementation OYObject
- (instancetype)init
{
    self = [super init];
    if (self) {
        _value = 0;
    }
    return self;
}

- (void)TestChange{
    [self willChangeValueForKey:@"value"];
    _value = 5;//直接為成員變量賦值
    [self didChangeValueForKey:@"value"];
}
@end

運行锦担,輸出:

Runtime-study[17800:14302974] value 新值:1
Runtime-study[17800:14302974] value 新值:5

這時我們發(fā)現(xiàn),直接給成員變量賦值KVO生效了慨削。

由此可以推知洞渔,在NSKVONotifying_OYObject 類重寫被觀察屬性的setter方法時:

1.被觀察屬性發(fā)生改變之前套媚,willChangeValueForKey:被調用,通知系統(tǒng)該 keyPath的屬性值即將變更磁椒;
2.當改變發(fā)生后堤瘤,didChangeValueForKey:被調用,通知系統(tǒng)該 keyPath 的屬性值已經變更浆熔;
3.之后本辐, observeValueForKey:ofObject:change:context: 也會被調用。且重寫觀察屬性的 setter 方法這種繼承方式的注入是在運行時而不是編譯時實現(xiàn)的医增。

KVO 為子類的觀察者屬性重寫調用存取方法的工作原理在代碼中相當于:

//NSKVONotifying_A的setter實現(xiàn)
-(void)setValue:(id)obj{
    [self willChangeValueForKey:@"keyPath"];    //KVO 在調用存取方法之前總調用
    [super setValue:newName forKey:obj]; //調用父類的實現(xiàn)慎皱,即原類的實現(xiàn)
    [self didChangeValueForKey:@"keyPath"];     //KVO 在調用存取方法之后總調用
}

總結:
1.使用setter方法改變值KVO會生效
2.使用setValue:forKey 改變值KVO會生效
3.成員變量直接修改值,KVO不會生效,需要手動添加KVO的兩個方法才會生效

參考文章iOS開發(fā) -- KVO的實現(xiàn)原理與具體應用

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末叶骨,一起剝皮案震驚了整個濱河市茫多,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌忽刽,老刑警劉巖天揖,帶你破解...
    沈念sama閱讀 217,277評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異跪帝,居然都是意外死亡今膊,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評論 3 393
  • 文/潘曉璐 我一進店門歉甚,熙熙樓的掌柜王于貴愁眉苦臉地迎上來万细,“玉大人,你說我怎么就攤上這事纸泄±党” “怎么了?”我有些...
    開封第一講書人閱讀 163,624評論 0 353
  • 文/不壞的土叔 我叫張陵聘裁,是天一觀的道長雪营。 經常有香客問我,道長衡便,這世上最難降的妖魔是什么献起? 我笑而不...
    開封第一講書人閱讀 58,356評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮镣陕,結果婚禮上谴餐,老公的妹妹穿的比我還像新娘。我一直安慰自己呆抑,他們只是感情好岂嗓,可當我...
    茶點故事閱讀 67,402評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著鹊碍,像睡著了一般厌殉。 火紅的嫁衣襯著肌膚如雪食绿。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,292評論 1 301
  • 那天公罕,我揣著相機與錄音器紧,去河邊找鬼。 笑死楼眷,一個胖子當著我的面吹牛铲汪,可吹牛的內容都是我干的。 我是一名探鬼主播罐柳,決...
    沈念sama閱讀 40,135評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼桥状,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了硝清?” 一聲冷哼從身側響起辅斟,我...
    開封第一講書人閱讀 38,992評論 0 275
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎芦拿,沒想到半個月后士飒,有當地人在樹林里發(fā)現(xiàn)了一具尸體,經...
    沈念sama閱讀 45,429評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡蔗崎,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,636評論 3 334
  • 正文 我和宋清朗相戀三年酵幕,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片缓苛。...
    茶點故事閱讀 39,785評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡芳撒,死狀恐怖,靈堂內的尸體忽然破棺而出未桥,到底是詐尸還是另有隱情笔刹,我是刑警寧澤,帶...
    沈念sama閱讀 35,492評論 5 345
  • 正文 年R本政府宣布冬耿,位于F島的核電站舌菜,受9級特大地震影響,放射性物質發(fā)生泄漏亦镶。R本人自食惡果不足惜日月,卻給世界環(huán)境...
    茶點故事閱讀 41,092評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望缤骨。 院中可真熱鬧爱咬,春花似錦、人聲如沸绊起。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至串前,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間实蔽,已是汗流浹背荡碾。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留局装,地道東北人坛吁。 一個月前我還...
    沈念sama閱讀 47,891評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像铐尚,于是被迫代替她去往敵國和親拨脉。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,713評論 2 354