KVC解析(六) —— KVC容器類及深層次原理

版本記錄

版本號 時間
V1.0 2017.09.12

前言

KVC相信大家再熟悉不過了虐急,鍵值編碼逻悠,可以解決很多問題,包括視圖上的給UITextField占位文字顏色大小進(jìn)行設(shè)置等等讯蒲,還有很多地方可以用KVC,接下來幾篇我們就深度解析一下KVC肄扎∧郑總結(jié)一下赁酝,就是指iOS的開發(fā)中,可以允許開發(fā)者通過Key名直接訪問對象的屬性旭等,或者給對象的屬性賦值酌呆。而不需要調(diào)用明確的存取方法。這樣就可以在運行時動態(tài)地訪問和修改對象的屬性搔耕。而不是在編譯時確定隙袁,這也是iOS開發(fā)中的黑魔法之一。還是老規(guī)矩弃榨,由面到點菩收,由淺到深,希望對大家有所幫助鲸睛。感興趣的可以看我寫的另外幾篇文章娜饵。
1. KVC解析(一) —— 基本了解
2. KVC解析(二) —— 不可不知的賦值深層次原理
3. KVC解析(三) —— 不可不知的取值深層次原理
4. KVC解析(四) —— keyPath的深度解析
5. KVC解析(五) —— KVC幾種典型的異常處理

容器類

容器類主要是指NSArray、NSMutableArray腊凶、NSDictionary划咐、NSMutableDictionary等有序容器和NSSet等無序容器拴念。


有序不可變?nèi)萜黝?/h1>

對于NSArray可以通過valueForKey:進(jìn)行訪問钧萍,如下代碼所示:

#import "JJKVCCollectionVC.h"

@interface JJKVCCollectionVC ()

@property (nonatomic, strong) NSArray *kvcArr;

@end

@implementation JJKVCCollectionVC

#pragma mark - Override Base Function

- (void)viewDidLoad
{
    [super viewDidLoad];
    
    self.view.backgroundColor = [UIColor whiteColor];
    
    [self demoArray];
    
}

#pragma mark - Object Private Function

- (void)demoArray
{
    self.kvcArr = @[@1, @2, @3, @4];
    
    NSObject *obj = [self valueForKey:@"kvcArr"];
    
    NSLog(@"obj = %@", obj);
}

@end

下面看輸出結(jié)果

2017-09-12 16:15:20.614 JJOC[3774:104163] obj = (
    1,
    2,
    3,
    4
)

對于NSDictionary也是一樣的,下面看一下代碼政鼠。

#import "JJKVCCollectionVC.h"

@interface JJKVCCollectionVC ()

@property (nonatomic, strong) NSDictionary *kvcDict;

@end

@implementation JJKVCCollectionVC

#pragma mark - Override Base Function

- (void)viewDidLoad
{
    [super viewDidLoad];
    
    self.view.backgroundColor = [UIColor whiteColor];

    [self demoDictionary];

}

#pragma mark - Object Private Function

- (void)demoDictionary
{
    self.kvcDict = @{@"1" : @"One", @"2" : @"Two", @"3" : @"Three"};
    
    NSObject *obj = [self valueForKey:@"kvcDict"];
    
    NSLog(@"obj = %@", obj);
}

@end

下面看輸出結(jié)果

2017-09-12 16:20:38.571 JJOC[4054:110989] obj = {
    1 = One;
    2 = Two;
    3 = Three;
}

從上面我們可以看出對于有序不可變?nèi)萜黝?code>NSArray和NSDictionary等等风瘦,都可以使用valueForKey :這個方法來獲取該屬性。


有序可變?nèi)萜黝?/h1>

下面我們看一下可變?nèi)萜黝惞悖热缯fNSMutableArrayNSMutableDictionary万搔。比如對于NSMutableArray可以使用下面方法獲取數(shù)組。

- (NSMutableArray *)mutableArrayValueForKey:(NSString *)key;

下面看示例代碼

#import "JJKVCCollectionVC.h"

@interface JJKVCCollectionVC ()

@property (nonatomic, strong) NSMutableArray *kvcArrM;

@end

@implementation JJKVCCollectionVC

#pragma mark - Override Base Function

- (void)viewDidLoad
{
    [super viewDidLoad];
    
    self.view.backgroundColor = [UIColor whiteColor];

    [self demoNSMutableArray];
}

#pragma mark - Object Private Function

- (void)demoNSMutableArray
{
    NSArray *arr = @[@1, @2, @3, @4];
    self.kvcArrM = [NSMutableArray arrayWithArray:arr];
    
    NSObject *obj = [self mutableArrayValueForKey:@"kvcArrM"];
    
    NSLog(@"obj = %@", obj);
}

@end

下面看輸出結(jié)果

2017-09-12 16:37:09.181 JJOC[4520:123892] obj = (
    1,
    2,
    3,
    4
)

下面我們看一下方法- (NSMutableArray *)mutableArrayValueForKey:(NSString *)key;的深層次原理官帘。

  • 搜索insertObject:in<Key>AtIndex: , removeObjectFrom<Key>AtIndex:或者 insert<Key>AdIndexes ,remove<Key>AtIndexes格式的方法瞬雹。如果至少找到一個insert方法和一個remove方法,那么同樣返回一個可以響應(yīng)NSMutableArray所有方法代理集合(類名是NSKeyValueFastMutableArray2)刽虹,那么給這個代理集合發(fā)送NSMutableArray的方法酗捌,以insertObject:in<Key>AtIndex:, removeObjectFrom<Key>AtIndex: 或者 insert<Key>AdIndexes , remove<Key>AtIndexes組合的形式調(diào)用。還有兩個可選實現(xiàn)的接口:replaceOnjectAtIndex:withObject:,replace<Key>AtIndexes:with<Key>:涌哲。

  • 如果上步的方法沒有找到胖缤,則搜索set<Key>: 格式的方法,如果找到阀圾,那么發(fā)送給代理集合的NSMutableArray最終都會調(diào)用set<Key>:方法哪廓。 也就是說,mutableArrayValueForKey:取出的代理集合修改后初烘,用set<Key>:重新賦值回去去涡真。這樣做效率會低很多分俯。所以推薦實現(xiàn)上面的方法。

  • 如果上一步的方法還還沒有找到,再檢查類方法+ (BOOL)accessInstanceVariablesDirectly豪治,如果返回YES(默認(rèn)行為)温治,會按_<key><key>,的順序搜索成員變量名橄登,如果找到,那么發(fā)送的NSMutableArray消息方法直接交給這個成員變量處理讥此。

  • 如果還是找不到拢锹,則調(diào)用valueForUndefinedKey:

  • 關(guān)于mutableArrayValueForKey:的適用場景萄喳,我在網(wǎng)上找了很多卒稳,發(fā)現(xiàn)其一般是用在對NSMutableArray添加Observer上。如果對象屬性是個NSMutableArray他巨、NSMutableSet充坑、NSMutableDictionary等集合類型時,你給它添加KVO時染突,你會發(fā)現(xiàn)當(dāng)你添加或者移除元素時并不能接收到變化捻爷。因為KVO的本質(zhì)是系統(tǒng)監(jiān)測到某個屬性的內(nèi)存地址或常量改變時,會添加上- (void)willChangeValueForKey:(NSString *)key- (void)didChangeValueForKey:(NSString *)key方法來發(fā)送通知份企,所以一種解決方法是手動調(diào)用者兩個方法也榄,但是并不推薦,你永遠(yuǎn)無法像系統(tǒng)一樣真正知道這個元素什么時候被改變司志。另一種便是利用使用mutableArrayValueForKey:了甜紫。

下面我們就直接看代碼

1. JJKVCCollectionVC.h
#import <UIKit/UIKit.h>

@interface JJKVCCollectionVC : UIViewController

- (void)demoMutableArrayValueForKey;

- (void)addItem;

- (void)addAntherItem;

- (void)removeLastItem;

@end
2. JJKVCCollectionVC.m
#import "JJKVCCollectionVC.h"

@interface JJKVCCollectionVC ()

@property (nonatomic, strong) NSMutableArray *kvcArrM;

@end

@implementation JJKVCCollectionVC

#pragma mark - Override Base Function

- (void)viewDidLoad
{
    [super viewDidLoad];
    
    self.view.backgroundColor = [UIColor whiteColor];
}

- (void)dealloc
{
    [self removeObserver:self forKeyPath:@"kvcArrM"];
}

#pragma mark - Object Private Function

- (void)demoMutableArrayValueForKey
{
    self.kvcArrM = [NSMutableArray array];
    [self addObserver:self forKeyPath:@"kvcArrM" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];
}

- (void)addItem
{
    [self.kvcArrM addObject:@"One"];
}

- (void)addAntherItem
{
    [[self mutableArrayValueForKey:@"kvcArrM"] addObject:@"Two"];
    [[self mutableArrayValueForKey:@"kvcArrM"] addObject:@"Three"];
}

- (void)removeLastItem
{
    [[self mutableArrayValueForKey:@"kvcArrM"] removeLastObject];
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context
{
    NSLog(@"change = %@", change);
    NSLog(@"arrM = %@", self.kvcArrM);
}

@end
3. JJKVCTestVC.m
#import "JJKVCTestVC.h"
#import "JJKVCCollectionVC.h"

@interface JJKVCTestVC ()

@end

@implementation JJKVCTestVC

- (void)viewDidLoad
{
    [super viewDidLoad];
    
    JJKVCCollectionVC *collectionVC = [[JJKVCCollectionVC alloc] init];
    
    [collectionVC demoMutableArrayValueForKey];
    [collectionVC addItem];
    [collectionVC addAntherItem];
    [collectionVC removeLastItem];
}

@end

下面我們看輸出結(jié)果

2017-09-12 17:55:38.831 JJOC[6637:175428] change = {
    indexes = "<_NSCachedIndexSet: 0x6000002232a0>[number of indexes: 1 (in 1 ranges), indexes: (1)]";
    kind = 2;
    new =     (
        Two
    );
}
2017-09-12 17:55:38.832 JJOC[6637:175428] arrM = (
    One,
    Two
)
2017-09-12 17:55:38.832 JJOC[6637:175428] change = {
    indexes = "<_NSCachedIndexSet: 0x600000223300>[number of indexes: 1 (in 1 ranges), indexes: (2)]";
    kind = 2;
    new =     (
        Three
    );
}
2017-09-12 17:55:38.832 JJOC[6637:175428] arrM = (
    One,
    Two,
    Three
)
2017-09-12 17:55:38.833 JJOC[6637:175428] change = {
    indexes = "<_NSCachedIndexSet: 0x600000223300>[number of indexes: 1 (in 1 ranges), indexes: (2)]";
    kind = 3;
    old =     (
        Three
    );
}
2017-09-12 17:55:38.833 JJOC[6637:175428] arrM = (
    One,
    Two
)

從上面可以看出,可變數(shù)組里面元素的改變可以被KVO監(jiān)聽到了骂远。當(dāng)調(diào)用[self.kvcArrM addObject:@"One"];時不會觸發(fā)KVO囚霸,只有調(diào)用方式改變?yōu)?code>[[self mutableArrayValueForKey:@"kvcArrM"] addObject:@"Two"];才會觸發(fā)KVO。


無序可變?nèi)萜黝?/h1>

對于無序可變?nèi)萜鞒S玫木褪?code>NSMutableSet激才,對于可變無序容器類和上面講的可變有序容器類的數(shù)組有點相似拓型,主要是下面這個方法。

- (NSMutableSet *)mutableSetValueForKey:(NSString *)key;

下面看一下調(diào)用該方法底層深層次的一些東西贸营。

  • 搜索addObject<Key>Object: , remove<Key>Object: 或者 add<Key> , remove<Key>格式的方法
    如果至少找到一個insert方法和一個remove方法吨述,那么同樣返回一個可以響應(yīng)NSMutableSet所有方法代理集合(類名是NSKeyValueFastMutableSet2),那么給這個代理集合發(fā)送NSMutableSet的方法钞脂,以addObject<Key>Object: , remove<Key>Object: 或者 add<Key> , remove<Key>組合的形式調(diào)用揣云。還有兩個可選實現(xiàn)的接口:intersect<Key> , set<Key>:

  • 如果receiverManagedObject冰啃,那么就不會繼續(xù)搜索邓夕。

  • 如果上一步的方法沒有找到刘莹,則搜索set<Key>: 格式的方法,如果找到焚刚,那么發(fā)送給代理集合的NSMutableSet最終都會調(diào)用set<Key>:方法点弯。 也就是說,mutableSetValueForKey取出的代理集合修改后矿咕,用set<Key>: 重新賦值回去去抢肛。這樣做效率會低很多。所以推薦實現(xiàn)上面的方法碳柱。

  • 如果上一步的方法還沒有找到捡絮,再檢查類方法+ (BOOL)accessInstanceVariablesDirectly,如果返回YES(默認(rèn)行為)莲镣,會按_<key>, <key>的順序搜索成員變量名福稳,如果找到,那么發(fā)送的NSMutableSet消息方法直接交給這個成員變量處理瑞侮。

  • 如果還是找不到的圆,調(diào)用valueForUndefinedKey:
    可見,除了檢查receiverManagedObject以外半火,其搜索順序和mutableArrayValueForKey基本一至越妈。

后記

這幾天回了老家沒有網(wǎng),博客更新的有點慢了慈缔,回來立刻就補充了一篇叮称,希望對大家有所幫助种玛,未完藐鹤,待續(xù)~~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市赂韵,隨后出現(xiàn)的幾起案子娱节,更是在濱河造成了極大的恐慌,老刑警劉巖祭示,帶你破解...
    沈念sama閱讀 216,651評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件肄满,死亡現(xiàn)場離奇詭異,居然都是意外死亡质涛,警方通過查閱死者的電腦和手機稠歉,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,468評論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來汇陆,“玉大人怒炸,你說我怎么就攤上這事≌贝” “怎么了阅羹?”我有些...
    開封第一講書人閱讀 162,931評論 0 353
  • 文/不壞的土叔 我叫張陵勺疼,是天一觀的道長。 經(jīng)常有香客問我捏鱼,道長执庐,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,218評論 1 292
  • 正文 為了忘掉前任导梆,我火速辦了婚禮轨淌,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘看尼。我一直安慰自己猿诸,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,234評論 6 388
  • 文/花漫 我一把揭開白布狡忙。 她就那樣靜靜地躺著梳虽,像睡著了一般。 火紅的嫁衣襯著肌膚如雪灾茁。 梳的紋絲不亂的頭發(fā)上窜觉,一...
    開封第一講書人閱讀 51,198評論 1 299
  • 那天,我揣著相機與錄音北专,去河邊找鬼禀挫。 笑死,一個胖子當(dāng)著我的面吹牛拓颓,可吹牛的內(nèi)容都是我干的语婴。 我是一名探鬼主播,決...
    沈念sama閱讀 40,084評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼驶睦,長吁一口氣:“原來是場噩夢啊……” “哼砰左!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起场航,我...
    開封第一講書人閱讀 38,926評論 0 274
  • 序言:老撾萬榮一對情侶失蹤缠导,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后溉痢,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體僻造,經(jīng)...
    沈念sama閱讀 45,341評論 1 311
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,563評論 2 333
  • 正文 我和宋清朗相戀三年孩饼,在試婚紗的時候發(fā)現(xiàn)自己被綠了髓削。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,731評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡镀娶,死狀恐怖立膛,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情汽畴,我是刑警寧澤旧巾,帶...
    沈念sama閱讀 35,430評論 5 343
  • 正文 年R本政府宣布耸序,位于F島的核電站,受9級特大地震影響鲁猩,放射性物質(zhì)發(fā)生泄漏坎怪。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,036評論 3 326
  • 文/蒙蒙 一廓握、第九天 我趴在偏房一處隱蔽的房頂上張望搅窿。 院中可真熱鬧,春花似錦隙券、人聲如沸男应。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,676評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽沐飘。三九已至,卻和暖如春牲迫,著一層夾襖步出監(jiān)牢的瞬間耐朴,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,829評論 1 269
  • 我被黑心中介騙來泰國打工盹憎, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留筛峭,地道東北人。 一個月前我還...
    沈念sama閱讀 47,743評論 2 368
  • 正文 我出身青樓陪每,卻偏偏與公主長得像影晓,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子檩禾,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,629評論 2 354

推薦閱讀更多精彩內(nèi)容