iOS-KVC(一)基本使用
iOS-KVC(二)內(nèi)部賦值深層次原理
iOS-KVC(三)內(nèi)部取值深層次原理
iOS-KVC(四)常見異常處理
iOS-KVC(五)容器類
iOS-KVC(六)正確性驗證
iOS-KVC(七)字典相關(guān)
iOS-KVC(八)常見使用
容器類主要是指NSArray、NSMutableArray、NSDictionary、NSMutableDictionary等有序容器和NSSet等無序容器。
對象的屬性可以是一對一的,也可以是一對多的。一對多的屬性要么是有序的(數(shù)組),要么是無序的(集合)。
不可變的有序容器屬性(NSArray)和無序容器屬性(NSSet)一般可以使用
- (id)valueForKey:(NSString *)key;
來獲取。
有序不可變?nèi)萜黝怤SArray, NSDictionary
#import "ViewController.h"
@interface ViewController ()
@property (nonatomic, strong) NSArray *items;
@property (nonatomic, strong) NSDictionary *dict;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self kvcArray];
NSLog(@"====================");
[self kvcDictionary];
}
- (void)kvcArray
{
self.items = @[@1, @2, @3];
NSLog(@"type=%@", NSStringFromClass([self.items class]));
NSLog(@"items=%@", [self valueForKey:@"items"]);
}
- (void)kvcDictionary
{
self.dict = @{@"one": @1, @"two": @2, @"three": @3};
NSLog(@"type=%@", NSStringFromClass([self.dict class]));
NSLog(@"dict=%@", [self valueForKey:@"dict"]);
}
@end
結(jié)果打?。?2019-06-23 10:40:14.883586+0800 study[4353:63534] type=__NSArrayI
2019-06-23 10:40:14.883836+0800 study[4353:63534] items=(
1,
2,
3
)
2019-06-23 10:40:14.883939+0800 study[4353:63534] ====================
2019-06-23 10:40:14.884056+0800 study[4353:63534] type=__NSDictionaryI
2019-06-23 10:40:14.884227+0800 study[4353:63534] dict={
one = 1;
three = 3;
two = 2;
}
有序可變?nèi)萜黝?NSMutableArray
當(dāng)對象的屬性是可變的容器時,對于有序的容器,可以用下面的方法
- (NSMutableArray *)mutableArrayValueForKey:(NSString *)key;
該方法返回一個可變有序數(shù)組,如果調(diào)用該方法,KVC的搜索順序如下
- 搜索
-insertObject:in<Key>AtIndex:
-removeObjectFrom<Key>AtIndex:
或者
-insert<Key>:atIndexes:
-remove<Key>AtIndexes:
格式的方法,如果至少找到一個insert方法和一個remove方法,那么同樣返回一個可以響應(yīng)NSMutableArray所有方法代理集合(類名是NSKeyValueFastMutableArray2),那么給這個代理集合發(fā)送NSMutableArray的方法,以上面兩種形式之一調(diào)用。
還有兩個可選實現(xiàn)的接口:
-replaceObjectIn<Key>AtIndex:withObject:
-replace<Key>AtIndexes:with<Key>:
如果上步的方法沒有找到,則搜索-set<Key>: 格式的方法,如果找到,那么發(fā)送給代理集合的NSMutableArray最終都會調(diào)用-set<Key>: 方法。也就是說,mutableArrayValueForKey:取出的代理集合修改后,用set<Key>: 重新賦值回去。
如果上一步的方法還還沒有找到,再檢查類方法+accessInstanceVariablesDirectly,如果返回YES(默認(rèn)返回YES),會按_<key>,<key>,的順序搜索成員變量名,如果找到,那么發(fā)送的NSMutableArray消息方法直接交給這個成員變量處理;如果還是找不到,則調(diào)用-valueForUndefinedKey:。
關(guān)于-mutableArrayValueForKey: 這個方法,其一般適用的場景是用在對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)一樣真正知道這個元素什么時候被改變。
另一種便是利用使用
- (NSMutableArray *)mutableArrayValueForKey:(NSString *)key;
簡單示例:
#import "ViewController.h"
@interface ViewController ()
@property (nonatomic, strong) NSMutableArray *items;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
//初始化
self.items = [NSMutableArray array];
[self addObserver:self forKeyPath:@"items" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:nil];
//操作
[self addItem];
[self addItemObserver];
[self removeItemObserver];
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context
{
NSLog(@"%@", change);
}
- (void)addItem{
[self.items addObject:@1];
}
- (void)addItemObserver {
[[self mutableArrayValueForKey:@"items"] addObject:@2];
}
- (void)removeItemObserver {
[[self mutableArrayValueForKey:@"items"] removeLastObject];
}
- (void)dealloc{
[self removeObserver:self forKeyPath:@"items"];
}
@end
打印結(jié)果:
2019-06-23 11:21:32.859338+0800 study[5377:79817] {
indexes = "<_NSCachedIndexSet: 0x600002da7ba0>[number of indexes: 1 (in 1 ranges), indexes: (1)]";
kind = 2;
new = (
2
);
}
2019-06-23 11:21:32.859668+0800 study[5377:79817] {
indexes = "<_NSCachedIndexSet: 0x600002da7ba0>[number of indexes: 1 (in 1 ranges), indexes: (1)]";
kind = 3;
old = (
2
);
}
結(jié)論:
當(dāng)普通地調(diào)用 [self.items addObject:@1]; ,Observer并不會回調(diào)。
只有使用了[[self mutableArrayValueForKey:@"items"] addObject:@2];,才能正確地觸- (NSMutableSet *)mutableSetValueForKey:(NSString *)key;發(fā)KVO。
無序可變?nèi)萜黝?NSMutableSet
對于無序的容器,可以用下面的方法
- (NSMutableSet *)mutableSetValueForKey:(NSString *)key;
該方法返回一個可變的無序數(shù)組如果調(diào)用該方法,KVC的搜索順序如下
- 搜索
-add<Key>Object:
-remove<Key>Object:
或者
-add<Key>:
-remove<Key>:
格式的方法
如果至少找到一個insert方法和一個remove方法,那么同樣返回一個可以響應(yīng)NSMutableSet所有方法代理集合(類名是NSKeyValueFastMutableSet2),那么給這個代理集合發(fā)送NSMutableSet的方法,以
-add<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>: 重新賦值回去。
如果上一步的方法還沒有找到,再檢查類方法+ (BOOL)accessInstanceVariablesDirectly,如果返回YES(默認(rèn)行為),會按_<key>, <key>的順序搜索成員變量名,如果找到,那么發(fā)送的NSMutableSet消息方法直接交給這個成員變量處理。
如果還是找不到,調(diào)用valueForUndefinedKey:
可見,除了檢查receiver是ManagedObject以外,其搜索順序和mutableArrayValueForKey基本一致。
同樣,它們也有對應(yīng)的keyPath版本
- (NSMutableArray *)mutableArrayValueForKeyPath:(NSString *)keyPath;
- (NSMutableSet *)mutableSetValueForKeyPath:(NSString *)keyPath;
有序集合和無序集合對應(yīng)方法如下:
有序集合對應(yīng)方法如下:
-countOf<Key>//必須實現(xiàn),對應(yīng)于NSArray的基本方法count:2 -objectIn<Key>AtIndex:
-<key>AtIndexes://這兩個必須實現(xiàn)一個,對應(yīng)于 NSArray 的方法 objectAtIndex: 和 objectsAtIndexes:
-get<Key>:range://不是必須實現(xiàn)的,但實現(xiàn)后可以提高性能,其對應(yīng)于 NSArray 方法 getObjects:range:
-insertObject:in<Key>AtIndex:
-insert<Key>:atIndexes://兩個必須實現(xiàn)一個,類似于 NSMutableArray 的方法 insertObject:atIndex: 和 insertObjects:atIndexes:
-removeObjectFrom<Key>AtIndex:
-remove<Key>AtIndexes://兩個必須實現(xiàn)一個,類似于 NSMutableArray 的方法 removeObjectAtIndex: 和 removeObjectsAtIndexes:
-replaceObjectIn<Key>AtIndex:withObject:
-replace<Key>AtIndexes:with<Key>://這兩個都是可選的
-countOf<Key>//必須實現(xiàn),對應(yīng)于NSArray的基本方法count:
-objectIn<Key>AtIndex:
-<key>AtIndexes://這兩個必須實現(xiàn)一個,對應(yīng)于 NSArray 的方法 objectAtIndex: 和 objectsAtIndexes:
-get<Key>:range://不是必須實現(xiàn)的,但實現(xiàn)后可以提高性能,其對應(yīng)于 NSArray 方法 getObjects:range:
-insertObject:in<Key>AtIndex:
-insert<Key>:atIndexes://兩個必須實現(xiàn)一個,類似于 NSMutableArray 的方法 insertObject:atIndex: 和 insertObjects:atIndexes:
-removeObjectFrom<Key>AtIndex:
-remove<Key>AtIndexes://兩個必須實現(xiàn)一個,類似于 NSMutableArray 的方法 removeObjectAtIndex: 和 removeObjectsAtIndexes:
-replaceObjectIn<Key>AtIndex:withObject:
-replace<Key>AtIndexes:with<Key>://這兩個都是可選的