KVC和KVO的使用及原理

關(guān)于KVC


KVC是什么?

Key-Value Coding,即鍵值編碼。它是一種不通過存取方法氯夷,而通過屬性名稱字符串間接訪問屬性的機(jī)制。

KVC常用的方法

前兩個方法無論獲取值還是賦值靶擦,只需要傳入屬性名稱的字符串就行了腮考。但KVC也提供了傳入path的方法。所謂path玄捕,就是用點(diǎn)號連接的多層級的屬性踩蔚,比如student.name,student屬性里的name屬性枚粘。

    - (id)valueForKey:(NSString *)key;
    - (void)setValue:(id)value forKey:(NSString *)key;

    - (id)valueForKeyPath:(NSString *)keyPath;
    - (void)setValue:(id)value forKeyPath:(NSString *)keyPath;
KVC對多種數(shù)據(jù)類型的支持

首先要說的是對于基本數(shù)據(jù)類型的屬性馅闽,KVC的這幾個方法會自動裝箱和拆箱。
其次馍迄,KVC也支持?jǐn)?shù)組和字典等集合數(shù)據(jù)福也。這里不多探討,關(guān)于這些知識可以看這篇博文:KVC/KVO原理詳解及編程指南

KVC的原理:KVC是怎么訪問屬性的攀圈?

KVC在某種程度上提供了替代存取方法(訪問器方法)的方案暴凑,不過存取方法終究是個好東西,以至于只要有可能赘来,KVC也盡可能先嘗試使用存取方法訪問屬性现喳。當(dāng)使用KVC訪問屬性時,它內(nèi)部其實(shí)做了很多事:
1.首先查找有無<property>撕捍,set<property>拿穴,is<property>等property屬性對應(yīng)的存取方法泣洞,若有忧风,則直接使用這些方法;
2.若無,則繼續(xù)查找_<property>球凰,_get<property>狮腿,set<property>等方法腿宰,若有就使用;
3.若查詢不到以上任何存取方法缘厢,則嘗試直接訪問實(shí)例變量<property>吃度,
<property>;
4.若連該成員變量也訪問不到贴硫,則會在下面方法中拋出異常椿每。之所以提供這兩個方法,就是讓你在因訪問不到該屬性而程序即將崩掉前英遭,供你重寫间护,在內(nèi)做些處理,防止程序直接崩掉挖诸。
valueForUndefinedKey:setValue:forUndefinedKey:方法汁尺。

關(guān)于KVO


KVO是什么?

Key-Value Obersver多律,即鍵值觀察痴突。它是觀察者模式的一種衍生±擒瘢基本思想是辽装,對目標(biāo)對象的某屬性添加觀察,當(dāng)該屬性發(fā)生變化時粘秆,會自動的通知觀察者如迟。這里所謂的通知是觸發(fā)觀察者對象實(shí)現(xiàn)的KVO的接口方法。
** KVO是解決model和view同步的好法子攻走。**
另外殷勘,KVO的優(yōu)點(diǎn)是當(dāng)被觀察的屬性值改變時是會自動發(fā)送通知的,這比通知中心需要post通知來說昔搂,簡單了許多玲销。

KVO怎么用?

1.首先給目標(biāo)對象的屬性添加觀察:

- (void)addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(nullable void *)context;

2.實(shí)現(xiàn)下面方法來接收通知摘符,需要注意各個參數(shù)的含義:

- (void)observeValueForKeyPath:(nullable NSString *)keyPath ofObject:(nullable id)object change:(nullable NSDictionary<NSString*, id> *)change context:(nullable void *)context;

3.最后要移除觀察者:

- (void)removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath;

舉一個??:

#import "ViewController.h"
#import "Student.h"


@interface ViewController ()
{
    Student             *_student;
}

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    _student = [[Student alloc] init];
    _student.stuName = @"oldName_hu";
    
    // 1.給student對象的添加觀察者贤斜,觀察其stuName屬性
    [_student addObserver:self forKeyPath:@"stuName" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:nil];
    
    // 此時,stuName發(fā)生了變化
    _student.stuName = @"newName_wang";
}

// stuName發(fā)生變化后逛裤,觀察者(self)立馬得到通知瘩绒。
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context
{
    // 最好判斷目標(biāo)對象object和屬性路徑keyPath
    if(object == _student && [keyPath isEqualToString:@"stuName"])
    {
        NSLog(@"----old:%@----new:%@",change[@"old"],change[@"new"]);
    }else
    {
        [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
    }
}


- (void)dealloc
{
    // 移除觀察者
    [_student removeObserver:self forKeyPath:@"stuName"];
}

@end

上面代碼需要注意的一點(diǎn)是接收通知的方法里最好判斷一下目標(biāo)對象和屬性路徑,因?yàn)橛锌赡苡卸鄠€KVO觀察多個對象的屬性带族;而且锁荔,父類也有可能使用了KVO哦,所以在else里蝙砌,對現(xiàn)有條件外的情況交給父類去處理阳堕。

KVO的原理:KVO是怎么實(shí)現(xiàn)的跋理?

當(dāng)某個類的對象第一次被觀察時,系統(tǒng)就會在運(yùn)行期動態(tài)地創(chuàng)建該類的一個派生類恬总,在這個派生類中重寫基類中被觀察屬性的 setter 方法前普,在setter方法里使其具有通知機(jī)制。因此壹堰,要想KVO生效拭卿,必須直接或間接的通過setter方法訪問屬性(KVC的setValue就是間接方式)。直接訪問成員變量KVO是不生效的贱纠。

同時派生類還重寫了 class 方法以“欺騙”外部調(diào)用者它就是起初的那個類记劈。然后系統(tǒng)將這個對象的 isa 指針指向這個新誕生的派生類,因此這個對象就成為該派生類的對象了并巍,因而在該對象上對 setter 的調(diào)用就會調(diào)用重寫的 setter目木,從而激活鍵值通知機(jī)制。此外懊渡,派生類還重寫了 dealloc 方法來釋放資源刽射。

重新的setter方法里到底干了什么,而使其就有了通知機(jī)制呢剃执?其實(shí)只是在setter方法里誓禁,給屬性賦值的前后分別調(diào)用了兩個方法

- (void)willChangeValueForKey:(NSString *)key;
- (void)didChangeValueForKey:(NSString *)key;

- (void)didChangeValueForKey:(NSString *)key;會調(diào)用

- (void)observeValueForKeyPath:(nullable NSString *)keyPath ofObject:(nullable id)object change:(nullable NSDictionary<NSString*, id> *)change context:(nullable void *)context;

這就是KVO實(shí)現(xiàn)的基本原理了!
當(dāng)沒有存取方法而通過KVC的setValue修改屬性值時肾档,同樣的在運(yùn)行時也會在setValue:forKey方法里默認(rèn)調(diào)用上面?zhèn)z方法摹恰。
** 其實(shí)我們也可以手動,顯式的調(diào)用這兩個方法怒见,以使其具有通知機(jī)制俗慈。**
下面用例子驗(yàn)證:

#import "ViewController.h"

@interface ViewController ()
{
    NSString            *_testStr;
}

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    // 給self的添加self觀察者,自己觀察自己的testStr成員變量
    [self addObserver:self forKeyPath:@"testStr" options:NSKeyValueObservingOptionNew context:nil];
    
    [self willChangeValueForKey:@"testStr"];
    _testStr = @"this is a test"; // 直接修改成員變量的值遣耍,但是顯式的闺阱、手動的調(diào)用上下倆方法,使其就有通知機(jī)制
    [self didChangeValueForKey:@"testStr"];
}


- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context
{
    if(object == self && [keyPath isEqualToString:@"testStr"])
    {
        NSLog(@"----new:%@----",change[@"new"]);
    }else
    {
        [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
    }
}


- (void)dealloc
{
    // 移除觀察者
    [self removeObserver:self forKeyPath:@"stuName"];
}

@end

引用:
詳解鍵值觀察(KVO)及其實(shí)現(xiàn)機(jī)理
KVC/KVO原理詳解及編程指南

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末舵变,一起剝皮案震驚了整個濱河市酣溃,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌纪隙,老刑警劉巖赊豌,帶你破解...
    沈念sama閱讀 207,248評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異绵咱,居然都是意外死亡碘饼,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,681評論 2 381
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來派昧,“玉大人,你說我怎么就攤上這事拢切〉傥” “怎么了?”我有些...
    開封第一講書人閱讀 153,443評論 0 344
  • 文/不壞的土叔 我叫張陵淮椰,是天一觀的道長五慈。 經(jīng)常有香客問我,道長主穗,這世上最難降的妖魔是什么泻拦? 我笑而不...
    開封第一講書人閱讀 55,475評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮忽媒,結(jié)果婚禮上争拐,老公的妹妹穿的比我還像新娘。我一直安慰自己晦雨,他們只是感情好架曹,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,458評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著闹瞧,像睡著了一般绑雄。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上奥邮,一...
    開封第一講書人閱讀 49,185評論 1 284
  • 那天万牺,我揣著相機(jī)與錄音,去河邊找鬼洽腺。 笑死脚粟,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的蘸朋。 我是一名探鬼主播珊楼,決...
    沈念sama閱讀 38,451評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼度液!你這毒婦竟也來了厕宗?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,112評論 0 261
  • 序言:老撾萬榮一對情侶失蹤堕担,失蹤者是張志新(化名)和其女友劉穎已慢,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體霹购,經(jīng)...
    沈念sama閱讀 43,609評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡佑惠,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,083評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片膜楷。...
    茶點(diǎn)故事閱讀 38,163評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡旭咽,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出赌厅,到底是詐尸還是另有隱情穷绵,我是刑警寧澤,帶...
    沈念sama閱讀 33,803評論 4 323
  • 正文 年R本政府宣布特愿,位于F島的核電站仲墨,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏揍障。R本人自食惡果不足惜目养,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,357評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望毒嫡。 院中可真熱鬧癌蚁,春花似錦、人聲如沸兜畸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,357評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽膳叨。三九已至洽洁,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間菲嘴,已是汗流浹背饿自。 一陣腳步聲響...
    開封第一講書人閱讀 31,590評論 1 261
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留龄坪,地道東北人昭雌。 一個月前我還...
    沈念sama閱讀 45,636評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像健田,于是被迫代替她去往敵國和親烛卧。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,925評論 2 344

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