一. KVC
間接的修改或獲取對(duì)象的屬性,降低程序(類與類)之間的耦合度.
常見(jiàn)的兩種用法
1.對(duì)私有變量進(jìn)行賦值,同樣的也可以通過(guò)它進(jìn)行取值
在使用KVC時(shí), 一定要保證鍵值是存在的.
#KVC最為重要的四個(gè)方法
- (nullable id)valueForKey:(NSString *)key; //直接通過(guò)Key來(lái)取值
- (void)setValue:(nullable id)value forKey:(NSString *)key; //通過(guò)Key來(lái)設(shè)值
- (nullable id)valueForKeyPath:(NSString *)keyPath; //通過(guò)KeyPath來(lái)取值
- (void)setValue:(nullable id)value forKeyPath:(NSString *)keyPath; //通過(guò)KeyPath來(lái)設(shè)值屬性
#NSKeyValueCoding類別中其他的一些方法:
//默認(rèn)返回YES赦拘,表示如果沒(méi)有找到Set<Key>方法的話像捶,會(huì)按照_key吴裤,_iskey铺罢,key,iskey的順序搜索成員,設(shè)置成NO就不這樣搜索
+ (BOOL)accessInstanceVariablesDirectly;
//KVC提供屬性值正確性驗(yàn)證的API听绳,它可以用來(lái)檢查set的值是否正確垃环、為不正確的值做一個(gè)替換值或者拒絕設(shè)置新值并返回錯(cuò)誤原因邀层。
- (BOOL)validateValue:(inout id __nullable * __nonnull)ioValue forKey:(NSString *)inKey error:(out NSError **)outError;
//這是集合操作的API,里面還有一系列這樣的API遂庄,如果屬性是一個(gè)NSMutableArray寥院,那么可以用這個(gè)方法來(lái)返回。
- (NSMutableArray *)mutableArrayValueForKey:(NSString *)key;
//如果Key不存在涛目,且沒(méi)有KVC無(wú)法搜索到任何和Key有關(guān)的字段或者屬性只磷,則會(huì)調(diào)用這個(gè)方法,默認(rèn)是拋出異常泌绣。
- (nullable id)valueForUndefinedKey:(NSString *)key;
//和上一個(gè)方法一樣,但這個(gè)方法是設(shè)值预厌。
- (void)setValue:(nullable id)value forUndefinedKey:(NSString *)key;
//如果你在SetValue方法時(shí)面給Value傳nil阿迈,則會(huì)調(diào)用這個(gè)方法
- (void)setNilValueForKey:(NSString *)key;
//輸入一組key,返回該組key對(duì)應(yīng)的Value,再轉(zhuǎn)成字典返回轧叽,用于將Model轉(zhuǎn)到字典苗沧。
- (NSDictionary<NSString *, id> *)dictionaryWithValuesForKeys:(NSArray<NSString *> *)keys;
例子
#import <Foundation/Foundation.h>
@interface Test: NSObject {
NSString *_name;
}
@end
@implementation Test
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
// insert code here...
//生成對(duì)象
Test *obj = [[Test alloc] init];
//通過(guò)KVC賦值name
[obj setValue:@"xiaoming" forKey:@"name"];
//通過(guò)KVC取值name打印
NSLog(@"obj的名字是%@", [obj valueForKey:@"name"]);
}
return 0;
}
2.字典轉(zhuǎn)模型
obj.name=dic[@"name"];
obj.sex=dic[@"sex"];
obj.age=dic[@"age"];
//將以上代碼替換為一行代碼
[obj setValuesForKeysWithDictionary:dic];
二. KVO
對(duì)目標(biāo)對(duì)象的某屬性添加觀察,當(dāng)該屬性發(fā)生變化時(shí)炭晒,通過(guò)觸發(fā)觀察者對(duì)象實(shí)現(xiàn)的KVO接口方法待逞,來(lái)自動(dòng)的通知觀察者
*KVO使用步驟:
/***
keyPath:描述將要觀察的屬性,相對(duì)于被觀察者网严。
options:KVO的一些屬性配置识樱;有四個(gè)選項(xiàng)。
NSKeyValueObservingOptionNew:change字典包括改變后的值
NSKeyValueObservingOptionOld:change字典包括改變前的值
NSKeyValueObservingOptionInitial:注冊(cè)后立刻觸發(fā)KVO通知
NSKeyValueObservingOptionPrior:值改變前是否也要通知(這個(gè)key決定了是否在改變前改變后通知兩次)
context: 上下文震束,這個(gè)會(huì)傳遞到訂閱著的函數(shù)中怜庸,用來(lái)區(qū)分消息,所以應(yīng)當(dāng)是不同的垢村。
***/
[stuaddObserver:selfforKeyPath:@"name"options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:@"myObserver"];
//當(dāng)指定鍵值發(fā)生改變時(shí), 會(huì)自動(dòng)調(diào)用這個(gè)方法
observeValueForKeyPath.
//所有觀察者模式的性能都不好,需要及時(shí)移除割疾。
removeObserver.
例子
//
// XNViewController.m
// KVC----KVO
//
// Created by neng on 14-6-21.
// Copyright (c) 2014年 neng. All rights reserved.
//
#import "XNViewController.h"
#import "XNPerson.h"
#import "XNStudent.h"
#import "XNBook.h"
@interface XNViewController ()
@end
/**
* KVC: Key Value Coding(鍵值編碼)
* 間接修改/獲取對(duì)象的屬性, 降低類與類之間的耦合度.
* KVO: Key Value Observer(鍵值觀察)
KVO通常用于觀察”對(duì)象的某個(gè)屬性“發(fā)生變化時(shí),及時(shí)做出響應(yīng)嘉栓!
而NSNotificationCenter是需要POST"通知字符串"(表示監(jiān)聽(tīng)的事件類型)的對(duì)象存在宏榕,通知中心才能夠工作!
*/
@implementation XNViewController
- (void)viewDidLoad {
[super viewDidLoad];
//1.簡(jiǎn)單的修改對(duì)象屬性
[self kvcDemo1];
//2.對(duì)于子類, 也能直接修改
[self kvcDemo2];
//KVO 演示
[self kvoDemo];
}
- (void)kvoDemo {
XNStudent *stu = [[XNStudent alloc] init];
stu.name = @"xuneng";
//設(shè)置監(jiān)聽(tīng)對(duì)象
//1》 負(fù)責(zé)觀察的對(duì)象 self
//2》 觀察的鍵值路徑
//3》 觀察的選項(xiàng)
//4》 上下文:通常用于區(qū)分不同的觀察事件
[stu addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:@"myObserver"];
stu.name = @"neng"; //修改
//所有觀察者模式的性能都不好, 需要及時(shí)移除
[stu removeObserver:self forKeyPath:@"name"];
stu.name = @"xu"; //移除后再次修改
}
/**
當(dāng)KVO指定的對(duì)象鍵值發(fā)生改變時(shí), 會(huì)自動(dòng)調(diào)用該方法
name: 觀察的鍵值
object: 觀察的對(duì)象
change: 修改的字典(新舊數(shù)值)
context: 指定觀察者時(shí)傳入的上下文
*/
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(voidvoid *)context {
NSLog(@"|-- %@ --|-- %@ --|-- %@ --|-- %@--|", keyPath, object, change, context);
}
/**
*簡(jiǎn)單的修改對(duì)象屬性
*/
- (void)kvcDemo1 {
//1.原始的方式. 點(diǎn)語(yǔ)法修改對(duì)象屬性的值
XNPerson *p1 = [[XNPerson alloc] init];
p1.name = @"xuneng";
p1.age = 24;
// NSLog(@"%@ , %d",p1.name,p1.age);
NSLog(@"KVC1 demo1--> %@", p1); //這樣直接調(diào)用description方法.
//2.KVC來(lái)修改
[p1 setValue:@"xxxx" forKeyPath:@"name"];
[p1 setValue:@"10" forKeyPath:@"age"];
NSLog(@"KVC2 demo1--> %@ , %d", p1.name, p1.age);
}
/**
*對(duì)于子類, 也能直接改
*/
- (void)kvcDemo2 {
//1.傳統(tǒng)方法
XNStudent *p1 = [[XNStudent alloc] init]; //學(xué)生
p1.name = @"student xuneng";
p1.age = 22;
// NSLog(@"%@ , %d",p1.name,p1.age);
NSLog(@"KVC1 demo2--> %@", p1); //這樣直接調(diào)用description方法.
//2.KVC來(lái)修改子類
[p1 setValue:@"xxxx" forKeyPath:@"name"];
[p1 setValue:@"10" forKeyPath:@"age"];
NSLog(@"KVC2 demo2--> %@ , %d", p1.name, p1.age);
}
@end
1. 如何手動(dòng)觸發(fā)KVO侵佃?
答: 被監(jiān)聽(tīng)的屬性的值被修改時(shí)麻昼,就會(huì)自動(dòng)觸發(fā)KVO。如果想要手動(dòng)觸發(fā)KVO趣钱,則需要我們自己調(diào)用willChangeValueForKey和didChangeValueForKey方法即可在不改變屬性值的情況下手動(dòng)觸發(fā)KVO涌献,并且這兩個(gè)方法缺一不可。
2. 直接修改成員變量會(huì)觸發(fā)KVO嗎首有?
答:不會(huì)觸發(fā)
3. 通過(guò)KVC修改屬性會(huì)觸發(fā)KVO么燕垃?
答:會(huì)觸發(fā)
三枢劝、NSNotification
使用觀察者模式來(lái)實(shí)現(xiàn)的用于跨層傳遞消息。往往也用NSNotification來(lái)實(shí)現(xiàn)解耦的目的卜壕。
常見(jiàn)的還有代理傳值您旁、block傳值等。(代理實(shí)現(xiàn)和block一般用于一對(duì)一的情況轴捎。)