iOS設計模式 —— KV0

 

刨根問底KVO

KVO 全稱 Key-Value Observing。中文叫鍵值觀察。KVO實際上是一種觀察者模式,觀察者在鍵值改變時會獲得通知,利用它能夠很容易實現視圖組件和數據模型的分離,當數據模型的屬性值改變以後做爲監聽器的視圖組件就會被激發,激發時就會回調監聽器自身。相比Notification,KVO更加的簡單直接。html

KVO的操做方法由NSKeyValueCoding提供,而他是NSObject的類別,也就是說ObjC中幾乎全部的對象都支持KVO操做。app

KVO的使用也很簡單,就是簡單的3步。函數

  1. 註冊須要觀察的對象的屬性addObserver:forKeyPath:options:context:
  2. 實現observeValueForKeyPath:ofObject:change:context:方法,這個方法當觀察的屬性變化時會自動調用.在這個方法中還經過NSKeyValueObservingOptionNew這個參數要求把新值在dictionary中傳遞過來。
  3. 取消註冊觀察removeObserver:forKeyPath:context:

咱們觀察下代碼實現,探究實現原理:ui

Person.hatom

1
2
3
4
5
6
7
8
9
#import <Foundation/Foundation.h>

@interface Person : NSObject

- (void)registerObserver;

@property (nonatomic, assign) NSInteger age;

@end

Person.mspa

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#import "Person.h"

@implementation Person

- (void)registerObserver {

[self addObserver:self forKeyPath:@"age" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:nil];
}

- (NSString *)description {
return [NSString stringWithFormat:@"%@,%ld",[self valueForKey:@"isa"],self.age];

}

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

if ([keyPath isEqualToString:@"age"]) {
NSLog(@"%@",change);
} else {
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
}
}

- (void)dealloc {
[self removeObserver:self forKeyPath:@"age"];
}

main函數指針

1
2
3
4
5
6
7
8
Person * p = [[Person alloc] init];
p.age = 20;
NSLog(@"%@",p);

[p registerObserver];

p.age = 30;
NSLog(@"%@",p);

運行程序打印出的log日誌爲:日誌

1
2
3
4
5
6
7
Person,20
{
kind = 1;
new = 30;
old = 20;
}
NSKVONotifying_Person,30

不要太驚訝,咱們慢慢解釋。code

我重寫的Person類的description方法,使用KVC拿到isa指針,獲取到self的類名,咱們發如今使用KVO以前類名是Person,但註冊了KVO以後,類名變成了NSKVONotifying_Personorm

主要是由於KVO的實現使用了isa-swizzling。在程序運行時Person會生成一個派生類NSKVONotifying_Person,在這個派生類中重寫基類中任何被觀察屬性的setter方法,用來欺騙系統頂替原先的類。在setter方法中實現真正的通知機制.

1
2
3
4
5
6
7
//能夠到僞代碼

- (void)setAge:(int)age
{
[super setAge:age];
[監聽器 observeValueForKeyPath:@"age" ofObject:self change:@{} context:nil];
}

咱們又能夠猜想,使用KVO,內部必定執行setter方法。

當咱們把上面代碼 p.age = 30;改爲p->_age = 30;

你會發現KVO的方法不走了,也證明了這點。

若是p不提供setAge,getAge方法,還想用KVO.

1
2
3
[p willChangeValueForKey:@"age"];
p->_age = 30;
[p didChangeValueForKey:@"age"];

蘋果官方KVO文檔:

https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/KeyValueObserving/KeyValueObserving.html

另外.....

個人願望是.......

世界和平.........

相關文章
相關標籤/搜索