版本記錄
版本號 | 時間 |
---|---|
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)萜黝惞悖热缯fNSMutableArray
和NSMutableDictionary
万搔。比如對于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>:
。如果
receiver
是ManagedObject
冰啃,那么就不會繼續(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:
可見,除了檢查receiver
是ManagedObject
以外半火,其搜索順序和mutableArrayValueForKey
基本一至越妈。
后記
這幾天回了老家沒有網(wǎng),博客更新的有點慢了慈缔,回來立刻就補充了一篇叮称,希望對大家有所幫助种玛,未完藐鹤,待續(xù)~~