45. addObserver:forKeyPath:options:context:各個(gè)參數(shù)的作用分別是什么,observer中需要實(shí)現(xiàn)哪個(gè)方法才能獲得KVO回調(diào)?
// 添加鍵值觀察
/*
1 觀察者,負(fù)責(zé)處理監(jiān)聽事件的對(duì)象
2 觀察的屬性
3 觀察的選項(xiàng)
4 上下文
*/
[self.person addObserver:self
forKeyPath:@"name"
options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld
context:@"Person Name"];
observer中需要實(shí)現(xiàn)該方法:
// 所有的 kvo 監(jiān)聽到事件,都會(huì)調(diào)用此方法
/*
1. 監(jiān)聽的被觀察者的屬性鍵路徑
2. 被觀察者對(duì)象
3. change 被觀察者對(duì)象屬性變化字典(新/舊)
4. 上下文,與監(jiān)聽的時(shí)候傳遞的一致
*/
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary *)change
context:(void *)context;
46. 如何手動(dòng)觸發(fā)一個(gè)value的KVO
自動(dòng)觸發(fā)是指類似這種場(chǎng)景:在注冊(cè) KVO 之前設(shè)置一個(gè)初始值,注冊(cè)之后,設(shè)置一個(gè)不一樣的值,就可以觸發(fā)了。
自動(dòng)觸發(fā) KVO 的原理
鍵值觀察通知依賴于 NSObject
的兩個(gè)方法:
willChangeValueForKey:
和
didChangevlueForKey:
在一個(gè)被觀察屬性發(fā)生改變之前, willChangeValueForKey:
一定會(huì)被調(diào)用,這就 會(huì)記錄舊的值。而當(dāng)改變發(fā)生后,
observeValueForKey:ofObject:change:context:
會(huì)被調(diào)用,繼而 didChangeValueForKey:
也會(huì)被調(diào)用。如果可以手動(dòng)實(shí)現(xiàn)這些調(diào)用,就可以實(shí)現(xiàn)“手動(dòng)觸發(fā)KVO”了。
//
// ViewController.m
// 46
//
// Created by zhaoyingxin on 16/8/22.
// Copyright ? 2016年 zhaoyingxin@aliyun.com. All rights reserved.
//
#import "ViewController.h"
@interface ViewController ()
@property (nonatomic, strong) NSDate *now;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self manualKVO];
}
- (void)manualKVO{
[self addObserver:self
forKeyPath:@"now"
options:NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew
context:@"self.now.kvo"];
NSLog(@"1");
[self willChangeValueForKey:@"now"]; // “手動(dòng)觸發(fā)self.now的KVO”,必寫。
NSLog(@"2");
[self didChangeValueForKey:@"now"]; // “手動(dòng)觸發(fā)self.now的KVO”,必寫。
NSLog(@"4");
}
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary<NSString *,id> *)change
context:(void *)context {
NSLog(@"3");
NSLog(@"keyPath = %@",keyPath);
NSLog(@"object = %@",object);
NSLog(@"change = %@",change);
NSLog(@"context = %@",context);
}
//2016-08-22 11:45:45.805 46[44377:802802] 1
//2016-08-22 11:45:45.805 46[44377:802802] 2
//2016-08-22 11:45:45.806 46[44377:802802] 3
//2016-08-22 11:45:45.806 46[44377:802802] keyPath = now
//2016-08-22 11:45:45.806 46[44377:802802] object = <ViewController: 0x7fb33a6317c0>
//2016-08-22 11:45:45.806 46[44377:802802] change = {
// kind = 1;
// new = "<null>";
// old = "<null>";
//}
//2016-08-22 11:45:45.806 46[44377:802802] context = self.now.kvo
//2016-08-22 11:45:45.807 46[44377:802802] 4
@end
“自動(dòng)觸發(fā)”的實(shí)現(xiàn)原理:
比如調(diào)用 setNow:
時(shí),系統(tǒng)還會(huì)以某種方式在中間插入 willChangeValueForKey:
、 didChangeValueForKey:
和 observeValueForKeyPath:ofObject:change:context:
的調(diào)用。
- (void)setNow:(NSDate *)aDate {
[self willChangeValueForKey:@"now"]; // 沒有必要
_now = aDate;
[self didChangeValueForKey:@"now"]; // 沒有必要
}
這完全沒有必要,不要這么做,這樣的話,KVO代碼會(huì)被調(diào)用兩次
。KVO在調(diào)用存取方法之前總是調(diào)用 willChangeValueForKey:
,之后總是調(diào)用 didChangeValueForkey:
。怎么做到的呢?答案是通過 isa 混寫(isa-swizzling)
48.KVC的keyPath中的集合運(yùn)算符如何使用?
1.必須用在集合對(duì)象上或普通對(duì)象的集合屬性上
2.簡(jiǎn)單集合運(yùn)算符有@avg, @count , @max , @min ,@sum,
3.格式 @"@sum.age"或 @"集合屬性.@max.age"
49. KVC和KVO的keyPath一定是屬性么?
KVO支持實(shí)例變量
51.apple用什么方式實(shí)現(xiàn)對(duì)一個(gè)對(duì)象的KVO?
Apple 的文檔對(duì) KVO 實(shí)現(xiàn)的描述:
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 ...
從Apple 的文檔可以看出:Apple 并不希望過多暴露 KVO 的實(shí)現(xiàn)細(xì)節(jié)。不過,要是借助 runtime 提供的方法去深入挖掘,所有被掩蓋的細(xì)節(jié)都會(huì)顯示:
當(dāng)給一個(gè)對(duì)象添加觀察者時(shí),一個(gè)新的類會(huì)被動(dòng)態(tài)創(chuàng)建。
這個(gè)類繼承自該對(duì)象原本的類,并重寫了被觀察屬性的 setter 方法。
重寫的 setter 方法會(huì)負(fù)責(zé)在調(diào)用原 setter 方法之前和之后,通知所有觀察者對(duì)象:屬性值的更改。
最后通過 isa 混寫(isa-swizzling) 把這個(gè)對(duì)象的 isa 指針
( isa 指針告訴 Runtime 系統(tǒng)這個(gè)對(duì)象的類是什么 )
指向這個(gè)新創(chuàng)建的子類,對(duì)象就神奇的變成了新創(chuàng)建的子類的實(shí)例。
Apple 使用了 isa 混寫(isa-swizzling)來實(shí)現(xiàn) KVO
把 didChangeValueForKey:
注釋掉, observeValueForKeyPath:ofObject:change:context:
不會(huì)執(zhí)行
observeValueForKeyPath:ofObject:change:context:
是在 didChangeValueForKey:
內(nèi)部觸發(fā)的操作
調(diào)用順序
willChangeValueForKey:
---> didChangeValueForKey:
--->
observeValueForKeyPath:ofObject:change:context:
“手動(dòng)觸發(fā)”的使用場(chǎng)景是什么?一般我們只在希望能控制“回調(diào)的調(diào)用時(shí)機(jī)”時(shí)才會(huì)這么做。
而“回調(diào)的調(diào)用時(shí)機(jī)”就是在你調(diào)用 didChangeValueForKey:
方法時(shí)