KVC是什么
KVC 全稱Key-Value Coding,俗稱"鍵值編碼"。它可以通過一個字符串(key)來訪問屬性或者成員變量。
- 在蘋果的官方文檔中是這樣描述KVC的:它是一種通過字符串描述符而不是通過調用訪問方法或者直接使用實例變量的非直接的訪問對象屬性的機制。
- 對于KVC的基本的方法都定義在NSKeyValueCoding的非正式協議中,并且NSObject默認實現了該協議。
- KVC不僅支持對象類型,也支持數值類型和結構體。非對象類型的參數和返回類型會自動封裝成NSValue或NSNumber類型。
KVC常用api
//賦值
-(void)setValue:(id)value forKeyPath:(NSString *)keyPath;
-(void)setValue:(id)value forKey:(NSString *)key;
//取值
-(id)valueForKeyPath:(NSSting *)keyPath;
-(id)valueForKey:(NSSting *)key;
KVC原理
kvc在取值和賦值的時候是按照一定的順序進行查找的
賦值原理
取值原理
注意:
1、kvc在賦值和取值的時候是按照順序進行的,可以自己建一個類把對應方法寫進去看下執行及順序。
2、賦值時如果找不到成員變量,不實現setValue:forUndefinedKey:方法會崩潰
如果成員變量是int等基本數據類型,會自動包裝成NSNumber類型返回
3、取值時如果找不到成員變量,不實現valueForUndefinedKey方法會崩潰
如果成員變量是int等基本數據類型,賦值nil,不實現setNilValueForKey:會崩潰
KVO是什么
KVO 全稱Key-Value Observing,俗稱"鍵值監聽",可以用于監聽某個對象屬性值的改變
KVC常用api
- (void)addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(nullable void *)context;
- (void)removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath context:(nullable void *)context;
- (void)removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath;
KVC原理
栗子:創建一個MJPerson類的對象,添加監聽
- (void)viewDidLoad {
[super viewDidLoad];
self.person1 = [[MJPerson alloc] init];
self.person1.age = 1;
// 給person1對象添加KVO監聽
NSKeyValueObservingOptions options = NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld;
[self.person1 addObserver:self forKeyPath:@"age" options:options context:@"123"];
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
self.person1.age = 20;
}
// 當監聽對象的屬性值發生改變時,就會調用
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context
{
NSLog(@"監聽到%@的%@屬性值改變了 - %@ - %@", object, keyPath, change, context);
}
- (void)dealloc {
//移除監聽
[self.person1 removeObserver:self forKeyPath:@"age"];
}
OC在運行時的時候自動生成了一個NSKVONotifying_MJPerson 類對象作為MJPerson類對象的子類對象,通過底層_NSSet**ValueAndNotify方法(這里是_NSSetIntValueAndNotify) 重寫了setAge:方法,進而實現KVO的監聽過程。
以下可理解為實現該過程的偽代碼
#import "MJPerson.h"
@interface NSKVONotifying_MJPerson : MJPerson
@end
#import "NSKVONotifying_MJPerson.h"
@implementation NSKVONotifying_MJPerson
- (void)setAge:(int)age
{
_NSSetIntValueAndNotify();
}
// 偽代碼
void _NSSetIntValueAndNotify()
{
[self willChangeValueForKey:@"age"];
[super setAge:age];
[self didChangeValueForKey:@"age"];
}
- (void)didChangeValueForKey:(NSString *)key
{
// 通知監聽器,某某屬性值發生了改變
[oberser observeValueForKeyPath:key ofObject:self change:nil context:nil];
}
監聽過程圖
驗證過程在 我的參考文獻1進行了很詳細的說明
KVO監聽數組
iOS默認不支持KVO的形式來監聽數組的變化,當我們想要使用KVO監聽數組的狀態時,要做到兩點:
1、要將監聽的數組封裝到model里,不能監聽UIViewController里面的數組
2、不能這樣[_model.modelArray addObject]方法,需要這樣調用
[[_model mutableArrayValueForKey:@"modelArray"] addObject:str];
@interface ObserverModel : NSObject
@property (strong,nonatomic)NSMutableArray *dataArray;
@end
@implementation ObserverModel
-(NSMutableArray *)dataArray{
if(!_dataArray){
_dataArray = [NSMutableArray array];
}
return _dataArray;
}
@end
- (void)viewDidLoad {
[super viewDidLoad];
self.observerModel = [[ObserverModel alloc] init];
[self.observerModel addObserver:self forKeyPath:@"dataArray" options:NSKeyValueObservingOptionNew context:nil];
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
[[self.observerModel mutableArrayValueForKeyPath:@"dataArray"] addObject:@"haha"];
// [[obserVer mutableArrayValueForKeyPath:@"dataArray"] removeObject:voiceContent];
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context
{
if ([keyPath isEqualToString:@"dataArray"]){
NSLog(@"%@",change[@"new"]);
}
}
-(void)dealloc{
if (self.observerModel != nil) {
[self.observerModel removeObserver:self forKeyPath:@"dataArray"];
}
}
參考文檔
http://www.lxweimin.com/p/8a9cd3d1bed7
http://www.lxweimin.com/p/4eae365accc1
http://www.lxweimin.com/p/f27d060fc1f1