KVO

KVO即key-value-observing,鍵值觀察赦颇,是一種觀察者模式的實現(xiàn)機制(另一種為Notification)芥玉。KVO提供了一種機制锹淌,指定一個被觀察對象(如student對象),當被觀察對象student的某個屬性(如name)發(fā)生改變時觀察者對象(如teacher)會收到通知垄琐,同時作出相應處理(這孩子边酒,怎么能隨便改名呢,把你家長叫來)狸窘。

1.KVO的應用步驟
  • 注冊觀察者墩朦,實施監(jiān)聽:
[student addObserver:teacher forKeyPath:@"name" options:NSKeyValueObservingOptionNew context:nil];

參數(shù)說明:
1)student:被觀察者對象
2)teacher:觀察者對象,該對象必須實現(xiàn)
observeValueForKeyPath:ofObject:change:context: 方法
3)forKeyPath:被觀察者的屬性名翻擒,必須和被觀察者屬性名相同
4)options:屬性配置氓涣,有四個值:

typedef NS_OPTIONS(NSUInteger, NSKeyValueObservingOptions) {
//接收方法中傳入屬性變化后的新值,鍵為NSKeyValueChangeNewKey
NSKeyValueObservingOptionNew = 0x01,
//接收方法中傳入屬性變化前的舊值韭寸,鍵為NSKeyValueChangeOldKey
NSKeyValueObservingOptionOld = 0x02,
//注冊之后會立刻調用接收方法春哨,可以在程序第一次運行時做一些初始化操作,
如果配置了NSKeyValueObservingOptionNew恩伺,change參數(shù)內容會包含新值
NSKeyValueObservingOptionInitial NS_ENUM_AVAILABLE(10_5, 2_0) = 0x04,
//接收方法會在屬性變化前后分別調用一次赴背,變化前的通知change參數(shù)包含鍵值對:notificationIsPrior = 1。
NSKeyValueObservingOptionPrior NS_ENUM_AVAILABLE(10_5, 2_0) = 0x08
}

5)context:接收一個C指針,可以為kvo的回調方法傳值凰荚。

  • 在回調方法處理屬性變化
    每當監(jiān)聽的keypath發(fā)生改變就會調用該方法:
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
if (context == &PrivateKVOContext) {
if ([keyPath isEqualToString:@"name"]) {
            NSString *oldName = change[NSKeyValueChangeOldKey];
            NSString *newName = change[NSKeyValueChangeNewKey];
      }
   }
}

參數(shù)說明:
1)keyPath:被監(jiān)聽的keyPath , 用來區(qū)分不同的KVO監(jiān)聽燃观;
2)object:被觀察對象,可以獲得修改后的屬性值便瑟;
3)change:保存信息改變的字典(可能有舊的值缆毁,新的值等)

默認change參數(shù)會包含一個NSKeyValueChangeKindKey鍵值對,傳遞被監(jiān)聽 屬性的變化類型:
enum {
//屬性被重新設置
NSKeyValueChangeSetting = 1,
//表示更改的是集合屬性到涂,分別代表插入脊框、刪除、替換操作
NSKeyValueChangeInsertion = 2,
NSKeyValueChangeRemoval = 3,
NSKeyValueChangeReplacement = 4
};
typedef NSUInteger NSKeyValueChange;

如果NSKeyValueChangeKindKey參數(shù)是針對集合屬性的三個之一践啄,change 參數(shù)還會包含一個NSKeyValueChangeIndexesKey鍵值對浇雹,表示變化的index。
4)context:上下文屿讽,可以用來區(qū)分不同的KVO監(jiān)聽昭灵。

  • 移除觀察者
    因為添加觀察者并不會retain,所以即使被觀察者被釋放了伐谈,其監(jiān)聽信息仍舊存在烂完,如果不將觀察者移除就會出現(xiàn)崩潰。
[student removeObserver:teacher forKeyPath:@"name" context:&PrivateKVOContext];
2.KVO的簡單應用實例

KVO的常用場景是在MVC中同步model和UI诵棵,實現(xiàn)這樣的需求:點擊view的時候更新model的(person)數(shù)據(jù)并觸發(fā)UI同步抠蚣。可以看到應用KVO輕松的監(jiān)聽到模型數(shù)據(jù)的變化非春,進而在回調中更新UI柱徙。


pic8-1.gif
@interface ViewController ()
- (IBAction)randomAge:(UIButton *)sender;
@property (weak, nonatomic) IBOutlet UILabel *ageLabel;
@property (strong, nonatomic) Person *person;
@end

@implementation ViewController

- (void)viewDidLoad {
[super viewDidLoad];
self.person = [[Person alloc]init];
//創(chuàng)建觀察者
[self.person addObserver:self
forKeyPath:@"myAge"
options:NSKeyValueObservingOptionNew
context:nil];

}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
//UI同步
self.ageLabel.text = [NSString stringWithFormat:@"%@",change[NSKeyValueChangeNewKey]];
}

- (IBAction)randomAge:(UIButton *)sender {
//更新model數(shù)據(jù)
self.person.myAge = arc4random() % 100 ;
}

- (void)dealloc {
//移除觀察者
[self removeObserver:self forKeyPath:@"myAge"];
}
@end
3.手動鍵值觀察

在以上KVO的應用中通過創(chuàng)建觀察者,在屬性變化時就會自動發(fā)出通知奇昙,而有些場景需要人為的控制通知的發(fā)送护侮,這需要重寫被觀察者對象屬性的getter/setter方法。

#import "Person.h"

@implementation Person
{
NSUInteger myAge;
}

- (NSUInteger)myAge {
return myAge;
}

- (void)setMyAge:(NSUInteger)newAge {
//發(fā)送通知:鍵值即將改變
[self willChangeValueForKey:@"myAge"];
myAge = newAge;
//發(fā)送通知:鍵值已經修改
[self didChangeValueForKey:@"myAge"];
}

//當設置鍵值之后储耐,通過此方法羊初,決定是否發(fā)送通知
+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key {
//當key為myAge時,手動發(fā)送通知
if ([key isEqualToString:@"myAge"]) {
return NO;
}
//當為其他key時什湘,自動發(fā)送通知
return [super automaticallyNotifiesObserversForKey:key];
}

@end
4.設置屬性之間的依賴

假設我要監(jiān)聽一個color屬性的變化长赞,而一個color又與三原色相關,每種原色又由不同的組分構成闽撤,如此我們就需要監(jiān)聽N個原色屬性的變化得哆,每個原色屬性變化就去設置color的值,我的天哟旗,太麻煩了贩据,事情總是有解決的辦法:KVO給我們提供了這種鍵之間的依賴方法

+ (NSSet *)keyPathsForValuesAffecting<Key>;
//設置屬性依賴栋操,屬性greenComponent,依賴于屬性lComponent和屬性aComponent
+ (NSSet *)keyPathsForValuesAffectingGreenComponent {
return [NSSet setWithObjects:@"lComponent", @"aComponent",nil];
}

+ (NSSet *)keyPathsForValuesAffectingRedComponent {
return [NSSet setWithObject:@"lComponent"];
}

+ (NSSet *)keyPathsForValuesAffectingBlueComponent {
return [NSSet setWithObjects:@"lComponent", @"bComponent", nil];
}

+ (NSSet *)keyPathsForValuesAffectingColor {
return [NSSet setWithObjects:@"redComponent", @"greenComponent",@"blueComponent", nil];
}

通過color的屬性依賴設置饱亮,在原色組分lComponent矾芙、aComponent、bComponent發(fā)生變化時觀察者仍能收到color變化的通知近上。

5.KVO機制

前面已經了解到KVO的基本用法剔宪,那么KVO底層是如何實現(xiàn)的呢?Let me think壹无,既然KVO能夠監(jiān)聽屬性的變化葱绒,那么在被觀察者屬性的set方法中判斷如果屬性值發(fā)生了變化就向觀察者發(fā)送通知就可以實現(xiàn),似乎很簡單格遭,但事實是KVO并沒有對被觀察者進行顯示地重寫set方法哈街,那應該在哪重寫set方法呢留瞳?
蘋果通過isa混寫(isa-swizzling)來實現(xiàn)KVO拒迅。當創(chuàng)建觀察者觀察一個對象person時,KVO機制會動態(tài)的創(chuàng)建一個名為NSKVONotifying_Person的新類(繼承自person的本類Person)并將對象person的isa指針從Person類指向NSKVONotifying_Person(在這里只需要知道對象isa指針指向哪個類她倘,對象就會到哪個類去尋找對應方法)璧微。到這里我們應該找到了重寫set方法的地方,只要在NSKVONotifying_Person中重寫了Person的對應觀察屬性name的set方法硬梁,在被觀察對象person的屬性name被修改的時候會調用NSKVONotifying_Person中該屬性的set方法前硫,在set方法中完成相應的通知工作從而實現(xiàn)了屬性變化的監(jiān)聽。由此可以知道只有當屬性值是通過set方法修改的時KVO才有效荧止,例如在Person中有這樣一個方法:

//不是通過set方法修改的屹电,不會觸發(fā)KVO
- (void)changeName {
_name = @"newName";
}

KVO測試:

//輔助代碼
static NSArray *ClassMethodNames(Class c) {
NSMutableArray *array = [NSMutableArray array];
unsigned int methodCount = 0;
Method *methodList = class_copyMethodList(c, &methodCount);
unsigned int i;
for (i = 0; i < methodCount; i++) {
[array addObject:NSStringFromSelector(method_getName(methodList[i]))];
}
free(methodList);
return array;
}

static void PrintDescription(NSString *name, id obj) {
struct objc_object *objcet = (__bridge struct objc_object *)obj;
Class cls = objcet->isa;
NSString *str = [NSString stringWithFormat:@"%@: %@\n\tNSObject class %s\n\tlibobjc class %s : super class %s\n\timplements methods <%@>",
name,
obj,
class_getName([obj class]),
class_getName(cls),
class_getName(class_getSuperclass(cls)),
[ClassMethodNames(cls) componentsJoinedByString:@", "]];
printf("%s\n", [str UTF8String]);
}

// 測試代碼
Person *person1 = [[Person alloc] init];
Person *person2 = [[Person alloc] init];

[person2 addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew context:NULL];
PrintDescription(@"person1", person1);
PrintDescription(@"person2", person2);
//輸出
person1: <Person: 0x60800002fec0>
NSObject class Person
libobjc class Person : super class NSObject
implements methods <.cxx_destruct, name, setName:>
person2: <Person: 0x60800002fee0>
NSObject class Person
libobjc class NSKVONotifying_Person : super class Person
implements methods <setName:, class, dealloc, _isKVOA>

可以看到被觀察者對像person2的類已經被改為NSKVONotifying_Person了,并且重寫了setName方法(在set方法里實現(xiàn)通知)跃巡。此外當我們試著打印person2對象的類([person2 class])時危号,仍然是Person。所以在NSKVONotifying_Person中除了重寫相應的setter素邪,還重寫了class方法外莲,讓它返回原先的類。

PS: I am xinghun who is on the road.

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末兔朦,一起剝皮案震驚了整個濱河市偷线,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌沽甥,老刑警劉巖声邦,帶你破解...
    沈念sama閱讀 210,978評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異摆舟,居然都是意外死亡亥曹,警方通過查閱死者的電腦和手機英融,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,954評論 2 384
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來歇式,“玉大人驶悟,你說我怎么就攤上這事〔氖В” “怎么了痕鳍?”我有些...
    開封第一講書人閱讀 156,623評論 0 345
  • 文/不壞的土叔 我叫張陵,是天一觀的道長龙巨。 經常有香客問我笼呆,道長,這世上最難降的妖魔是什么旨别? 我笑而不...
    開封第一講書人閱讀 56,324評論 1 282
  • 正文 為了忘掉前任诗赌,我火速辦了婚禮,結果婚禮上秸弛,老公的妹妹穿的比我還像新娘铭若。我一直安慰自己,他們只是感情好递览,可當我...
    茶點故事閱讀 65,390評論 5 384
  • 文/花漫 我一把揭開白布叼屠。 她就那樣靜靜地躺著,像睡著了一般绞铃。 火紅的嫁衣襯著肌膚如雪镜雨。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,741評論 1 289
  • 那天儿捧,我揣著相機與錄音荚坞,去河邊找鬼。 笑死菲盾,一個胖子當著我的面吹牛颓影,可吹牛的內容都是我干的。 我是一名探鬼主播亿汞,決...
    沈念sama閱讀 38,892評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼瞭空,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了疗我?” 一聲冷哼從身側響起咆畏,我...
    開封第一講書人閱讀 37,655評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎吴裤,沒想到半個月后旧找,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經...
    沈念sama閱讀 44,104評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡麦牺,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,451評論 2 325
  • 正文 我和宋清朗相戀三年钮蛛,在試婚紗的時候發(fā)現(xiàn)自己被綠了鞭缭。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,569評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡魏颓,死狀恐怖岭辣,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情甸饱,我是刑警寧澤沦童,帶...
    沈念sama閱讀 34,254評論 4 328
  • 正文 年R本政府宣布,位于F島的核電站叹话,受9級特大地震影響偷遗,放射性物質發(fā)生泄漏。R本人自食惡果不足惜驼壶,卻給世界環(huán)境...
    茶點故事閱讀 39,834評論 3 312
  • 文/蒙蒙 一氏豌、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧热凹,春花似錦泵喘、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,725評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至股冗,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間和蚪,已是汗流浹背止状。 一陣腳步聲響...
    開封第一講書人閱讀 31,950評論 1 264
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留攒霹,地道東北人怯疤。 一個月前我還...
    沈念sama閱讀 46,260評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像催束,于是被迫代替她去往敵國和親集峦。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,446評論 2 348

推薦閱讀更多精彩內容

  • 本文結構如下: Why? (為什么要用KVO) What? (KVO是什么) How? ( KVO怎么用) Mo...
    等開會閱讀 1,639評論 1 21
  • 上半年有段時間做了一個項目抠刺,項目中聊天界面用到了音頻播放塔淤,涉及到進度條,當時做android時候處理的不太好速妖,由于...
    DaZenD閱讀 3,014評論 0 26
  • 本文由我們團隊的 糾結倫 童鞋撰寫高蜂。 文章結構如下: Why? (為什么要用KVO) What? (KVO是什么...
    知識小集閱讀 7,409評論 7 105
  • 一、概述 KVO罕容,即:Key-Value Observing备恤,它提供一種機制稿饰,當指定的對象的屬性被修改后,則其觀察...
    DeerRun閱讀 10,046評論 11 33
  • 1.KVC 關于 KVC 和 KVO ,我之前的總結文章有寫過,但是趨于表面惭笑,沒有探究其內部真正的實現(xiàn)原理和進階用...
    Liberalism閱讀 1,076評論 0 5