iOS-KVC(五)容器类

iOS-KVC(一)基本使用
iOS-KVC(二)内部赋值深层次原理
iOS-KVC(三)内部取值深层次原理
iOS-KVC(四)常见异常处理
iOS-KVC(五)容器类
iOS-KVC(六)正确性验证
iOS-KVC(七)字典相关
iOS-KVC(八)常见使用

容器类主要是指NSArray、NSMutableArray、NSDictionary、NSMutableDictionary等有序容器和NSSet等无序容器。

对象的属性可以是一对一的,也可以是一对多的。一对多的属性要么是有序的(数组),要么是无序的(集合)。
不可变的有序容器属性(NSArray)和无序容器属性(NSSet)一般可以使用

- (id)valueForKey:(NSString *)key;

来获取。

有序不可变容器类NSArray, 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

结果打印:
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;
}
有序可变容器类 NSMutableArray

当对象的属性是可变的容器时,对于有序的容器,可以用下面的方法

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

该方法返回一个可变有序数组,如果调用该方法,KVC的搜索顺序如下

  1. 搜索
-insertObject:in<Key>AtIndex: 
-removeObjectFrom<Key>AtIndex:

或者

-insert<Key>:atIndexes:
-remove<Key>AtIndexes:

格式的方法,如果至少找到一个insert方法和一个remove方法,那么同样返回一个可以响应NSMutableArray所有方法代理集合(类名是NSKeyValueFastMutableArray2),那么给这个代理集合发送NSMutableArray的方法,以上面两种形式之一调用。
还有两个可选实现的接口:

-replaceObjectIn<Key>AtIndex:withObject: 
-replace<Key>AtIndexes:with<Key>:
  1. 如果上步的方法没有找到,则搜索-set<Key>: 格式的方法,如果找到,那么发送给代理集合的NSMutableArray最终都会调用-set<Key>: 方法。也就是说,mutableArrayValueForKey:取出的代理集合修改后,用set<Key>: 重新赋值回去。

  2. 如果上一步的方法还还没有找到,再检查类方法+accessInstanceVariablesDirectly,如果返回YES(默认返回YES),会按_<key>,<key>,的顺序搜索成员变量名,如果找到,那么发送的NSMutableArray消息方法直接交给这个成员变量处理;如果还是找不到,则调用-valueForUndefinedKey:。

关于-mutableArrayValueForKey: 这个方法,其一般适用的场景是用在对NSMutableArray添加Observer上。

如果对象属性是个NSMutableArray、NSMutableSet、NSMutableDictionary等集合类型时,你给它添加KVO时,你会发现当你添加或者移除元素时并不能接收到变化。
因为KVO的本质是系统监测到某个属性的内存地址或常量改变时,会添加上

- (void)willChangeValueForKey:(NSString *)key;
- (void)didChangeValueForKey:(NSString *)key;

方法来发送通知,所以一种解决方法是手动调用者两个方法,但是并不推荐,你永远无法像系统一样真正知道这个元素什么时候被改变。

另一种便是利用使用

- (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

打印结果:
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
    );
}

结论:
当普通地调用 [self.items addObject:@1]; ,Observer并不会回调。
只有使用了[[self mutableArrayValueForKey:@"items"] addObject:@2];,才能正确地触- (NSMutableSet *)mutableSetValueForKey:(NSString *)key;发KVO。
无序可变容器类 NSMutableSet

对于无序的容器,可以用下面的方法

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

该方法返回一个可变的无序数组如果调用该方法,KVC的搜索顺序如下

  • 搜索
-add<Key>Object:
-remove<Key>Object:

或者

-add<Key>:
-remove<Key>:

格式的方法
如果至少找到一个insert方法和一个remove方法,那么同样返回一个可以响应NSMutableSet所有方法代理集合(类名是NSKeyValueFastMutableSet2),那么给这个代理集合发送NSMutableSet的方法,以

-add<Key>Object:
-remove<Key>Object:,
或者
-add<Key>:
-remove<Key>:

组合的形式调用。
还有两个可选实现的接口:

-intersect<Key>: 
-set<Key>:
  • 如果receiver是ManagedObject,那么就不会继续搜索。

  • 如果上一步的方法没有找到,则搜索set<Key>: 格式的方法,如果找到,那么发送给代理集合的NSMutableSet最终都会调用set<Key>:方法。 也就是说,mutableSetValueForKey取出的代理集合修改后,用set<Key>: 重新赋值回去。

  • 如果上一步的方法还没有找到,再检查类方法+ (BOOL)accessInstanceVariablesDirectly,如果返回YES(默认行为),会按_<key>, <key>的顺序搜索成员变量名,如果找到,那么发送的NSMutableSet消息方法直接交给这个成员变量处理。

  • 如果还是找不到,调用valueForUndefinedKey:
    可见,除了检查receiver是ManagedObject以外,其搜索顺序和mutableArrayValueForKey基本一致。

同样,它们也有对应的keyPath版本

- (NSMutableArray *)mutableArrayValueForKeyPath:(NSString *)keyPath;
- (NSMutableSet *)mutableSetValueForKeyPath:(NSString *)keyPath;

有序集合和无序集合对应方法如下:

有序集合对应方法如下:
 
-countOf<Key>//必须实现,对应于NSArray的基本方法count:2  -objectIn<Key>AtIndex:
 
-<key>AtIndexes://这两个必须实现一个,对应于 NSArray 的方法 objectAtIndex: 和 objectsAtIndexes:
 
-get<Key>:range://不是必须实现的,但实现后可以提高性能,其对应于 NSArray 方法 getObjects:range:
 
-insertObject:in<Key>AtIndex:
 
-insert<Key>:atIndexes://两个必须实现一个,类似于 NSMutableArray 的方法 insertObject:atIndex: 和 insertObjects:atIndexes:
 
-removeObjectFrom<Key>AtIndex:
 
-remove<Key>AtIndexes://两个必须实现一个,类似于 NSMutableArray 的方法 removeObjectAtIndex: 和 removeObjectsAtIndexes:
 
-replaceObjectIn<Key>AtIndex:withObject:
 
-replace<Key>AtIndexes:with<Key>://这两个都是可选的
-countOf<Key>//必须实现,对应于NSArray的基本方法count:
 
-objectIn<Key>AtIndex:
 
-<key>AtIndexes://这两个必须实现一个,对应于 NSArray 的方法 objectAtIndex: 和 objectsAtIndexes:
 
-get<Key>:range://不是必须实现的,但实现后可以提高性能,其对应于 NSArray 方法 getObjects:range:
 
-insertObject:in<Key>AtIndex:
 
-insert<Key>:atIndexes://两个必须实现一个,类似于 NSMutableArray 的方法 insertObject:atIndex: 和 insertObjects:atIndexes:
 
-removeObjectFrom<Key>AtIndex:
 
-remove<Key>AtIndexes://两个必须实现一个,类似于 NSMutableArray 的方法 removeObjectAtIndex: 和 removeObjectsAtIndexes:
 
-replaceObjectIn<Key>AtIndex:withObject:
 
-replace<Key>AtIndexes:with<Key>://这两个都是可选的
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,589评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,615评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,933评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,976评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,999评论 6 393
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,775评论 1 307
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,474评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,359评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,854评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,007评论 3 338
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,146评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,826评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,484评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,029评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,153评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,420评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,107评论 2 356

推荐阅读更多精彩内容