先聊聊 KVO 與 KVC 的區別吧:
KVO是指鍵-值-觀察者模式, 鍵值監聽, 監聽一個對象屬性值的改變。KVO是基于KVC的。
KVC 是指鍵-值編碼, 通過一個字符串的 key 來找到value , 是 value for key 方法, 直接或通過實例變量訪問的機制 。利用 KVC 可以隨意修改一個對象的屬性或者成員變量, 并且私有變量也可修改。
一. KVO
KVO是指鍵-值-觀察者(key-value-observe),
是一種使對象獲取其他對象的特定屬性變化的通知機制。
與NSNotification 不同的是。KVO 不需要通知中心對象。而是在對象屬性變化之后會直接通知觀察者。
KVO的步驟:
**1. 注冊觀察者 **
為了正確接收屬性的變化通知,觀察者對象必須先發一個消息給被觀察者對象
- (void)addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(nullable void *)context;
/*
NSKeyValueObservingOptions 可選的是一個枚舉值。我們通常用到的是兩個
NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew
當屬性發生變化時。我們可以把舊值和新值傳遞給觀察者
*/
**2.接收變化通知 **
應該注意的是如果只是使用成員變量改變值的話是不會觸發KVO的。要使用點語法,或者是KVC的方式改變值
// object 是被監聽對象
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary<NSString *,id> *)change
context:(void *)context;
**3.移除觀察者身份 **
在不需要觀察時要進行移除
- (void)removeObserver:(NSObject *)observer
forKeyPath:(NSString *)keyPath;
二.KVC
什么是KVC
KVC是指key-value-code,鍵-值編碼,是一種用于間接訪問對象屬性的機制。使用KVC可以直接修改對象屬性,即使是私有的也可以訪問。 如果是基本數據類型的應該封裝一下。
KVC的基本使用有下面幾點:
鍵值訪問
路徑訪問
取數組內的數據
一些簡單的運算
下面按照這幾點用法來介紹一下
// 為了方便以后操作,我們先簡單定義一下幾個類
// Person類
#import <Foundation/Foundation.h>
#import "Totoro.h"
@interface Person : NSObject
@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) NSUInteger age;
@property (nonatomic, strong) Totoro *Totoro;
@property (nonatomic, strong) NSArray *Totoros;
@end
// Totoro類
#import <Foundation/Foundation.h>
@interface Totoro : NSObject
@property (nonatomic, assign) NSUInteger weight;
@end
// 為了證明上面說的可以修改私有屬性,我們為 Totoro 添加一個私有的屬性
#import "Totoro.h"
@interface Totoro ()
@property (nonatomic, copy) NSString *name;
@end
@implementation Totoro
@end
基本設置完了之后,我們可以在Main函數里面進行一下操作
// 先創建一個人對象
Person *me = [[Person alloc] init];
Totoro *Totoro = [[Totoro alloc] init];
me.Totoro = Totoro; // 我養了一只龍貓
按照我們最為傳統的賦值方法我們要給人賦值一個name的話 我們通常會使用點語法進行賦值
// 本質上是調用了 [me setName:@"smile麗"];
me.name = @"smile麗"; // 這里相當于調用了setter方法
// 本質上是調用了 NSLog(@"%@", [me name]);
NSLog(@"%@", me.name); // 這里相當于調用getter方法
1> 鍵值訪問
那我們來看一下 使用KVC的方式應該如何去賦值.使用KVC, 會自動尋找成員變量(xxx),如果找不到,然后再去掉去尋找,如果再找不到,就會報錯。而不是去調用setter 和 getter 方法
// @() 對基本數據類型封裝成對象
[me setValue:@(24) forKey:@"age"];
NSLog(@"%@", [me valueForKey:@"age"]);
2> 路徑訪問
什么是路徑訪問,對于一個類來說,可能他的屬性是其他的類,如果要修改這里的屬性。我們需要先通過路徑來尋找到該屬性,然后再進行賦值.
[me setValue:@"大白" forKeyPath:@"totoro.name"]; // 注意這里的(.)只是路徑不是點語法
NSLog(@"%@", [me valueForKeyPath:@"totoro.name"]);
3> 取數組的數據
對于如果我們的數組里面存放的是對象,如果我們要獲取數組里面每個對象的屬性。這樣的話,最容易的方法就是遍歷數組。然后取出每個屬性進行添加到數組中。這時候我們也可以使用KVC快速解決這種問題.
為了測試,我給Person一個totoros的數組屬性,下面造一下數據.
NSMutableArray *totoros = [NSMutableArray array];
for (int i = 0; i < 4; i++) {
Totoro *totoro = [[totoro alloc] init];
NSString *name = [NSString stringWithFormat:@"大白_%d", i];
[totoro setValue:name forKey:@"name"];
NSUInteger weight = 3.8 + i;
[totoro setValue:@(weight) forKey:@"weight"]; // 這個數據在第四點用到
[totoros addObject:totoro];
}
我們如何實現上述的需求呢
使用 KVC回去屬性的數組
NSMutableArray *array = [totoros mutableArrayValueForKeyPath:@"name"];
NSLog(@"%@", array);
/*
(
"大白_0",
"大白_1",
"大白_2",
"大白_3"
)
*/
4> 一些簡單的運算
可以使用的關鍵字: 數量@count, 最大值@max, 最小值@min, 和@sum
me.totoros = totoros;
// 取到所有的相關屬性元素,進行計算, 由于方法返回的是 id, 所以要使用對象接收,我們可以使用 NSNumber, 而不是 int 之類的
NSLog(@"數量:%@", [p valueForKeyPath:@"totoros.@count"]);
NSLog(@"平均體重%@", [p valueForKeyPath:@"totoros.@avg.weight"]);