先聊聊 KVO 與 KVC 的區(qū)別吧:
KVO是指鍵-值-觀察者模式, 鍵值監(jiān)聽, 監(jiān)聽一個對象屬性值的改變池摧。KVO是基于KVC的荔泳。
KVC 是指鍵-值編碼, 通過一個字符串的 key 來找到value , 是 value for key 方法, 直接或通過實例變量訪問的機(jī)制 橙数。利用 KVC 可以隨意修改一個對象的屬性或者成員變量, 并且私有變量也可修改尊流。
一. KVO
KVO是指鍵-值-觀察者(key-value-observe),
是一種使對象獲取其他對象的特定屬性變化的通知機(jī)制。
與NSNotification 不同的是灯帮。KVO 不需要通知中心對象崖技。而是在對象屬性變化之后會直接通知觀察者逻住。
KVO的步驟:
**1. 注冊觀察者 **
為了正確接收屬性的變化通知,觀察者對象必須先發(fā)一個消息給被觀察者對象
- (void)addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(nullable void *)context;
/*
NSKeyValueObservingOptions 可選的是一個枚舉值迎献。我們通常用到的是兩個
NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew
當(dāng)屬性發(fā)生變化時瞎访。我們可以把舊值和新值傳遞給觀察者
*/
**2.接收變化通知 **
應(yīng)該注意的是如果只是使用成員變量改變值的話是不會觸發(fā)KVO的。要使用點語法忿晕,或者是KVC的方式改變值
// object 是被監(jiān)聽對象
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary<NSString *,id> *)change
context:(void *)context;
**3.移除觀察者身份 **
在不需要觀察時要進(jìn)行移除
- (void)removeObserver:(NSObject *)observer
forKeyPath:(NSString *)keyPath;
二.KVC
什么是KVC
KVC是指key-value-code装诡,鍵-值編碼,是一種用于間接訪問對象屬性的機(jī)制践盼。使用KVC可以直接修改對象屬性鸦采,即使是私有的也可以訪問。 如果是基本數(shù)據(jù)類型的應(yīng)該封裝一下咕幻。
KVC的基本使用有下面幾點:
鍵值訪問
路徑訪問
取數(shù)組內(nèi)的數(shù)據(jù)
一些簡單的運(yùn)算
下面按照這幾點用法來介紹一下
// 為了方便以后操作渔伯,我們先簡單定義一下幾個類
// 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
// 為了證明上面說的可以修改私有屬性,我們?yōu)?Totoro 添加一個私有的屬性
#import "Totoro.h"
@interface Totoro ()
@property (nonatomic, copy) NSString *name;
@end
@implementation Totoro
@end
基本設(shè)置完了之后肄程,我們可以在Main函數(shù)里面進(jìn)行一下操作
// 先創(chuàng)建一個人對象
Person *me = [[Person alloc] init];
Totoro *Totoro = [[Totoro alloc] init];
me.Totoro = Totoro; // 我養(yǎng)了一只龍貓
按照我們最為傳統(tǒng)的賦值方法我們要給人賦值一個name的話 我們通常會使用點語法進(jìn)行賦值
// 本質(zhì)上是調(diào)用了 [me setName:@"smile麗"];
me.name = @"smile麗"; // 這里相當(dāng)于調(diào)用了setter方法
// 本質(zhì)上是調(diào)用了 NSLog(@"%@", [me name]);
NSLog(@"%@", me.name); // 這里相當(dāng)于調(diào)用getter方法
1> 鍵值訪問
那我們來看一下 使用KVC的方式應(yīng)該如何去賦值.使用KVC锣吼, 會自動尋找成員變量(xxx),如果找不到蓝厌,然后再去掉去尋找玄叠,如果再找不到,就會報錯拓提。而不是去調(diào)用setter 和 getter 方法
// @() 對基本數(shù)據(jù)類型封裝成對象
[me setValue:@(24) forKey:@"age"];
NSLog(@"%@", [me valueForKey:@"age"]);
2> 路徑訪問
什么是路徑訪問读恃,對于一個類來說,可能他的屬性是其他的類代态,如果要修改這里的屬性寺惫。我們需要先通過路徑來尋找到該屬性,然后再進(jìn)行賦值.
[me setValue:@"大白" forKeyPath:@"totoro.name"]; // 注意這里的(.)只是路徑不是點語法
NSLog(@"%@", [me valueForKeyPath:@"totoro.name"]);
3> 取數(shù)組的數(shù)據(jù)
對于如果我們的數(shù)組里面存放的是對象蹦疑,如果我們要獲取數(shù)組里面每個對象的屬性西雀。這樣的話,最容易的方法就是遍歷數(shù)組歉摧。然后取出每個屬性進(jìn)行添加到數(shù)組中艇肴。這時候我們也可以使用KVC快速解決這種問題.
為了測試,我給Person一個totoros的數(shù)組屬性叁温,下面造一下數(shù)據(jù).
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"]; // 這個數(shù)據(jù)在第四點用到
[totoros addObject:totoro];
}
我們?nèi)绾螌崿F(xiàn)上述的需求呢
使用 KVC回去屬性的數(shù)組
NSMutableArray *array = [totoros mutableArrayValueForKeyPath:@"name"];
NSLog(@"%@", array);
/*
(
"大白_0",
"大白_1",
"大白_2",
"大白_3"
)
*/
4> 一些簡單的運(yùn)算
可以使用的關(guān)鍵字: 數(shù)量@count, 最大值@max, 最小值@min, 和@sum
me.totoros = totoros;
// 取到所有的相關(guān)屬性元素,進(jìn)行計算, 由于方法返回的是 id, 所以要使用對象接收,我們可以使用 NSNumber, 而不是 int 之類的
NSLog(@"數(shù)量:%@", [p valueForKeyPath:@"totoros.@count"]);
NSLog(@"平均體重%@", [p valueForKeyPath:@"totoros.@avg.weight"]);