如果你從事iOS開(kāi)發(fā),
對(duì)于KVO肯定不陌生.
今天寫了這篇文章,
讓我們進(jìn)一步了解KVO,
我們從下圖幾個(gè)部分了解KVO,
如果你看了這篇文章,
會(huì)顛覆你對(duì)KVO的認(rèn)知,
原來(lái)你了解過(guò)得KVO只是一部分.
1.什么是KVO?
KVO(Key Value Observing, 鍵值觀察)是Objective-C對(duì)觀察者模式的實(shí)現(xiàn),每次當(dāng)被觀察對(duì)象的某個(gè)屬性值發(fā)生改變時(shí),注冊(cè)的觀察者便能獲得通知。
作為一個(gè)開(kāi)發(fā)者,有一個(gè)學(xué)習(xí)的氛圍跟一個(gè)交流圈子特別重要,這是一個(gè)我的iOS交流群:638302184,不管你是小白還是大牛歡迎入駐 ,分享BAT,阿里面試題、面試經(jīng)驗(yàn),討論技術(shù), 與2800+iOS開(kāi)發(fā)者一起交流學(xué)習(xí)成長(zhǎng)!
2.KVO的使用
這里我們結(jié)合代碼了解KVO,
單純的文字描述已經(jīng)沒(méi)有發(fā)言權(quán).
- 案例一:
1.1我們先創(chuàng)建一個(gè)person類
#import <Foundation/Foundation.h>
@interface Person : NSObject{
@property(nonatomic,copy)NSString *name;
@end
#import "Person.h"
@implementation Person
@end
并為person添加屬性name
此時(shí),我們已經(jīng)創(chuàng)建好了person類
1.2接著,我們?cè)趘iewDidLoad方法中創(chuàng)建Person對(duì)象
//方便引用
@property(nonatomic,strong)Person *p;
Person *p = [[Person alloc]init];
//引用
_p = p;
p指針在哪個(gè)地方? p指針指向引用數(shù)據(jù)類型,對(duì)象在堆區(qū),指針在棧區(qū).
1.3接著我們?cè)赩iewDidLoad方法中添加觀察
[圖片上傳失敗...(image-532afe-1577458318977)]
[p addObserver:self forKeyPath:@"name" options:(NSKeyValueObservingOptionNew) context:nil];
1.4使用touchesBegan時(shí)間,改變屬性值,以方便進(jìn)行觀察.
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
static int a;
_p.name = [NSString stringWithFormat:@"%d",a++];
}
1.5此時(shí)我們運(yùn)行會(huì)不會(huì)有結(jié)果呢?
答案是我們點(diǎn)擊屏幕的時(shí)候,程序會(huì)崩潰.因?yàn)槲覀冞@里沒(méi)有去響應(yīng).
我們要添加響應(yīng)的方法
//響應(yīng)方法
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{
//觀察者觀察name的變化,當(dāng)點(diǎn)擊屏幕,改變name的值,chang就會(huì)捕獲新值.
NSLog(@"%@",change);
}
1.6,此時(shí)你運(yùn)行起來(lái)點(diǎn)屏幕幾下.控制臺(tái)就會(huì)有輸出了.
2018-03-31 18:38:54.319824+0800 001--kvo使用[2725:50940] {
kind = 1;
new = 0;
}
案例一,創(chuàng)建了基本簡(jiǎn)答的KVO.
- 案例二
如果我們改變了name的屬性,observe回調(diào)就來(lái)了,這就是自動(dòng)觸發(fā).
還有只用,變化了不一定每一次都通知,平常開(kāi)發(fā)需求也會(huì)遇到.
如果滿足了某一個(gè)條件的時(shí)候,我們來(lái)通知一下.
2.1我們?cè)趐erson中添加一個(gè)方法
+(BOOL)automaticallyNotifiesObserversForKey:(NSString *)key{
//手動(dòng)觸發(fā)
return NO;
//自動(dòng)觸發(fā)
// return YES;
}
2.2這個(gè)時(shí)候,我們回到ViewController中,對(duì),touchBegan方法進(jìn)行修改,就可以完成手動(dòng)觸發(fā).
static int a;
//手動(dòng)觸發(fā)
//即將改變
[_p willChangeValueForKey:@"name"]
_p.name = [NSString stringWithFormat:@"%d",a++];
// 改變完成
[_p didChangeValueForKey:@"name"];
```
2.3如果我們想滿足一個(gè)條件后,進(jìn)行屬性觀察.
我們就要在 automaticallyNotifiesObserversForKey:(NSString *)key
通過(guò)判斷key來(lái)進(jìn)行手動(dòng)觸發(fā)或者自動(dòng)觸發(fā).
比如,我們判斷是name的時(shí)候,我們手動(dòng)觸發(fā)
//手動(dòng)觸發(fā)
if([key isEqualToString:@"name"]){
return NO;
}
return YES;
```
案例二,手動(dòng)觸發(fā),自動(dòng)觸發(fā)
- 案例三
我們創(chuàng)建一個(gè)dog類,.我們想觀察person中的dog屬性的變化,我們可以這樣注冊(cè).
3.1首先,我們先創(chuàng)建dog類
#import <Foundation/Foundation.h>
@interface Dog : NSObject
@property(nonatomic,assign)int age;
@end
#import "Dog.h"
@implementation Dog
@end
3.2我們?cè)趐erson中加入
@property(nonatomic,strong)Dog *dog;
3.3對(duì)dog進(jìn)行初始化
-(instancetype)init{
if(self=[super init]){
_dog=[[Dog alloc]init];
}
return self;
}
3.4我們添加觀察者
[p addObserver:self forKeyPath:@"dog.age" options:(NSKeyValueObservingOptionNew) context:nil];
3.5在點(diǎn)擊事件中,改變age的值
_p.dog.age = a++;
我們觀察到dog的age值變化
- 案例四:
4.1我們?cè)赿og類中加入level
@property(nonatomic,assign)int level;
4.2通過(guò)person中的dog屬性,我們觀察dog中的level和age屬性的變化
[p addObserver:self forKeyPath:@"dog.age" options:(NSKeyValueObservingOptionNew) context:nil];
[p addObserver:self forKeyPath:@"dog.level" options:(NSKeyValueObservingOptionNew) context:nil];
4.3在點(diǎn)擊事件中,改變age和level
// _p.dog.age = a++;
// _p.dog.level = a++;
4.4運(yùn)行后,我們就可以看到屬性值的變化.
案例四,是不是有些麻煩呢,這種方式我們要寫兩次
4.KVO觀察容器
1.KVO --響應(yīng)式編程
首先,我們需要添加,訂閱,然后有響應(yīng)的方法
代理是么?其實(shí)代理也一樣
比如tableView,我們添加代理,就可以使用代理方法,代理方法回調(diào).
比如button時(shí)間,其實(shí)也是響應(yīng)式編程,為button添加target,開(kāi)啟方法,方法調(diào)用.2.我們?cè)诎咐刑岬搅藬?shù)組,我們初始化后并沒(méi)有觀察到person中array屬性的變化
我們這次就要借助KVC
2.1我們創(chuàng)建person類,加入arr屬性
@property(nonatomic,strong)NSMutableArray *arr;
2.2初始化數(shù)組
- (instancetype)init
{
self = [super init];
if (self) {
_arr = NSMutableArray.array;
}
return self;
}
2.3在VC中
@property(nonatomic,strong)Person *p;
2.4創(chuàng)建person對(duì)象,添加觀察者
Person *p = [[Person alloc]init];
_p = p;
[_p addObserver:self forKeyPath:@"name" options:(NSKeyValueObservingOptionNew) context:nil];
2.5響應(yīng)方法
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{
NSLog(@"%@",change);
}
2.6touchesBegan中加入
//KVO監(jiān)聽(tīng)容器方法,需要借助KVC
NSMutableArray *tempArr = [_p mutableArrayValueForKeyPath:@"arr"];
[tempArr addObject:@"123"];
2.7程序跑起來(lái),我們?cè)诳刂婆_(tái)可以看到
2018-03-31 21:15:13.551030+0800 003-KVO觀察容器[3369:118039] {
indexes = "<_NSCachedIndexSet: 0x60400003ff60>[number of indexes: 1 (in 1 ranges), indexes: (0)]";
kind = 2;
new = (
11
)
```
這里的kind是什么意思?
在案例1中輸出的kind=1,這里的kind=2.這代表了什么意思?
//set方法,kind=1
NSKeyValueChangeSetting = 1,
//插入
NSKeyValueChangeInsertion = 2,
//刪除
NSKeyValueChangeRemoval = 3,
//替換
NSKeyValueChangeReplacement = 4,
有沒(méi)有顛覆KVO調(diào)setter方法,還可以調(diào)用insert,remove,replace
這里輸出的kind2,說(shuō)明了插入的數(shù)據(jù)的時(shí)候,觀察者也可以觀察到屬性值的變化.
如果沒(méi)有深入了解一下KVO,知道了KVO不僅可以調(diào)用setter方法,還可以調(diào)用插入,刪除,代替方法.
### 總結(jié)
1.動(dòng)態(tài)創(chuàng)建Person的子類使用
2.子類重寫setName
3.動(dòng)態(tài)修改了對(duì)象的類型
4.還了解到了,KVO不僅可以調(diào)用setter方法,還可以調(diào)用插入,刪除,代替方法.通過(guò)對(duì)kvo的進(jìn)一步了解,我們是很清楚KVO底層運(yùn)用.
好的,這就是KVO.
這篇文章能幫到您,
請(qǐng)您點(diǎn)個(gè)贊