美文网首页iOS
iOS-KVC(五)容器类

iOS-KVC(五)容器类

作者: 厦门_小灰灰 | 来源:发表于2019-06-23 18:05 被阅读0次

    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>://这两个都是可选的
    

    相关文章

      网友评论

        本文标题:iOS-KVC(五)容器类

        本文链接:https://www.haomeiwen.com/subject/pnhiqctx.html