美文网首页
[iOS] KVO监听数组

[iOS] KVO监听数组

作者: manajay | 来源:发表于2018-04-30 13:11 被阅读502次

    对于swift,因为数组是值类型, 操作数组时,didSet 属性观察就可以很容易做到这一点.

    对于Objective-C ,必须要使用 KVO 才能 监听数组, 而且一般的操作只能监听对象指针的改变, 指针所指向的内容变化却无从察觉.

    • 那么重点来了 ,代码在下面
    #import "TodoStore.h"
    #import "LJConst.h"
    #import "TodosChangeNotifyInfo.h"
    
    @interface TodoStore()
    /**
     todo数据源
     */
    @property (nonatomic, strong, readwrite) NSMutableArray<TodoItem *> *todos;
    @end
    
    @implementation TodoStore
    
    #pragma mark - Life Circle
    + (instancetype)sharedInstance
    {
        static id sharedInstance = nil;
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            sharedInstance = [[self alloc] init];
        });
        return sharedInstance;
    }
    
    - (instancetype)init {
        if (self = [super init]) {
            [self configData];
            [self configObserver];
        }
        return self;
    }
    
    - (void)configData {
        self.todos = [NSMutableArray arrayWithCapacity:5];
    }
    
    #pragma mark - KVO
    - (void)configObserver {
        [self addObserver:self forKeyPath:NSStringFromSelector(@selector(todos)) options: NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld  context:nil];
    }
    
    - (void)dealloc {
        [self removeObserver:self forKeyPath: NSStringFromSelector(@selector(todos))];
    }
    
    ///  相关键值描述见 [KVO](http://yulingtianxia.com/blog/2014/05/12/objective-czhong-de-kvche-kvo/)
    - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
        // 其余的情况暂时不考虑, 例如,new & old 为数组的情况
        
        // 增加的
        NSArray *new = [change valueForKey: NSKeyValueChangeNewKey];
        NSLog(@"new : %@", new);
        // 移除的
        NSArray *old = [change valueForKey: NSKeyValueChangeOldKey];
        NSLog(@"old : %@", old);
    
        // 变更的索引
        NSIndexSet *indexes = [change valueForKey: NSKeyValueChangeIndexesKey];
        NSLog(@"indexes: %@",indexes);
        
        // 变更的类型
        NSKeyValueChange kind = [[change valueForKey: NSKeyValueChangeKindKey] unsignedIntegerValue];
        NSLog(@"kind: %lu",kind);
    
        switch (kind) {
            case NSKeyValueChangeInsertion  :
                [self changgsPostNotificationWithChangeType: TodosChangeTypeAdd changeIndexSet:indexes];
                break;
            case NSKeyValueChangeRemoval    :
                [self changgsPostNotificationWithChangeType: TodosChangeTypeRemove changeIndexSet:indexes];
                break;
            case NSKeyValueChangeSetting    :
                
                break;
            case NSKeyValueChangeReplacement:
                [self changgsPostNotificationWithChangeType: TodosChangeTypeReload changeIndexSet:indexes];
                break;
        }
        
    }
    
    - (void)changgsPostNotificationWithChangeType:(TodosChangeType) changeType changeIndexSet: (NSIndexSet *) changeIndexSet{
        TodosChangeNotifyInfo *notifyInfo = [TodosChangeNotifyInfo new];
        notifyInfo.changeType     = changeType;
        notifyInfo.changeIndexSet = changeIndexSet;
        
        NSDictionary *userInfo = @{kUserInfoNotifyInfoKey: notifyInfo};
        
        [[NSNotificationCenter defaultCenter] postNotificationName:kTodosChangeNotificationName object:nil userInfo:userInfo];
    }
    
    #pragma mark - Action
    - (void)addItems:(NSArray<TodoItem *> *)todoItems {
        [[self kvcTodos] addObjectsFromArray:todoItems];
    }
    
    - (void)addItem:(TodoItem *)todoItem {
        [[self kvcTodos] addObject:todoItem];
    }
    
    - (void)removeItems:(NSArray<TodoItem *> *)todoItems {
        [[self kvcTodos] removeObjectsInArray:todoItems];
    }
    
    - (void)removeItem:(TodoItem *)todoItem {
        [[self kvcTodos] removeObject:todoItem];
    }
    
    - (void)removeItemAtIndex:(NSUInteger)index {
        [[self kvcTodos] removeObjectAtIndex:index];
    }
    
    - (void)editItem:(TodoItem *)original new:(TodoItem *)new {
        NSUInteger index = [[self kvcTodos] indexOfObject:original];
        [self kvcTodos][index] = new;
    }
    
    #pragma mark - 只读
    
    - (NSUInteger)count{
        return self.todos.count;
    }
    
    - (NSArray<TodoItem *> *)todoItems {
        return self.todos.copy;
    }
    
    - (TodoItem *)todoAtIndex:(NSUInteger)index {
        return self.todos[index];
    }
    
    
    /**
     只用于内部KVO监听
     通过KVC获取数组,可以触发KKVO监听
     @return todo数组
     */
    - (NSMutableArray<TodoItem *> *)kvcTodos {
        return [self mutableArrayValueForKeyPath:NSStringFromSelector(@selector(todos))];
    }
    
    @end
    
    • 注意使用KVO, 必须要在适当的位置移除观察者, 且keypath不建议使用字符串. 可以使用@selector之类的小技巧.

    源码见 manajay/iOS-demos/MVC-onevcat

    相关文章

      网友评论

          本文标题:[iOS] KVO监听数组

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