目錄
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.這個中間類冰肴,繼承自原本的那個類。
代碼演示:
"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類的實例對象.
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的兩個方法才會生效