美文网首页
iOS-KVO相关

iOS-KVO相关

作者: 漆黑烈焰武士G | 来源:发表于2019-08-17 18:42 被阅读0次

    KVO相关


    一、KVO初探 — 响应观察

    (一)KVO 使用 的 三部曲

    1、添加观察

    - (void)viewDidLoad 
    {
        [super viewDidLoad];
        self.person  = [Person new];
        [self.person addObserver:self
                      forKeyPath:@"name"
                         options:(NSKeyValueObservingOptionNew)
                         context:"person_name"];
      //context  :  标签  --  区分 -> 继承、多监听
      //不用context可能会先需要遍历类的列表,再遍历属性列表等(context是便遍历啥?)
    }
    
    //插曲:nil和NULL的区别?
    

    2、响应

    - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
    {
        self.person.name = @"hehe";
    }
    
    //回调
    - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context
    {
        // 是如何来的 -- KVO 做了什么事情
        NSLog(@"%@ -- %@",change,object);
    }
    

    3、析构

    //不移除观察不一定会崩溃,但是会重复触发,引起很多问题
    //崩溃原因:因为没有源码,猜测是访问野指针导致,多发于单例中添加观察
    - (void)dealloc
    {
        [self.person removeObserver:self forKeyPath:@"name"];
    }
    
    (二)其他附加说明

    1、自动观察与手动观察

    Person.m
      
    + (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key
    {
        return YES;//若返回NO则所有观察均不响应
    }
    
    + (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key
    {
        if ([key isEqualToString:@"age"])
        {
            return YES;
        }
        return NO;
        //只观察age
    }
    
    //  ↑  自动观察  ↑
    // ---  分割线  ---
    //  ↓  手动观察  ↓
    
    - (void)setName:(NSString *)name
    {
        //即使自动观察关闭,也可以使用手动观察
        [self willChangeValueForKey:@"name"];
        _name = name;
        [self didChangeValueForKey:@"name"];
    }
    

    2、联动观察

    // 下载进度 -- writtenData/totalData
    //一旦你註冊了觀察者,協議就會調用 keysPathsForValuesAffectingValueForKey 對於你的特定屬性鍵。 如果這裡方法返回一組關鍵路徑,如果對屬性的任何直接更改通知你,則將為你的屬性發出更改通知,如果更改了這些路徑,則會發出更改通知。
    + (NSSet<NSString *> *)keyPathsForValuesAffectingValueForKey:(NSString *)key
    {
        NSSet *keyPaths = [super keyPathsForValuesAffectingValueForKey:key];
        if ([key isEqualToString:@"downloadProgress"]) 
        {
            NSArray *affectingKeys = @[@"totalData", @"writtenData"];
            keyPaths = [keyPaths setByAddingObjectsFromArray:affectingKeys];
        }
        return keyPaths;
    }
    
    //疑问:若改变totalData的值后再给downloadProgress复制会触发两遍监听吗?
    //答:会,相当于"downloadProgress"、"totalData"、"writtenData"三者无论谁改变都会触发对"downloadProgress"的监听
    

    3、观察集合类型 — 一定要通过KVC来取值,不然获取不到

    [self.person.dateArray addObject:@"hello"]; // 无效
    [[self.person mutableArrayValueForKey:@"dateArray"] addObject:@"hello"]; //有效
    
    //-----------
    
    //原因:
    /* Possible values in the NSKeyValueChangeKindKey entry in change dictionaries. See the comments for -observeValueForKeyPath:ofObject:change:context: for more information.
    */
    typedef NS_ENUM(NSUInteger, NSKeyValueChange) {
        NSKeyValueChangeSetting = 1,
        NSKeyValueChangeInsertion = 2,
        NSKeyValueChangeRemoval = 3,
        NSKeyValueChangeReplacement = 4,
    };
    //疑问:具体为啥.dateArray的addObject:不行但是mutableArrayValueForKey的addObject:可以?还得再研究研究
    

    二、探索KVO底层原理

    1、KVO默认观察setter方法

    self.person.nickName = @"KC";  //属性   观察到了
    self.person->name    = @"hehe";  //实例  没观察到
    
    // 说明:KVO默认观察setter方法
    

    2、动态类NSKVONotifying_XXX

    po self.person
    <Person: 0xxxxxxx>
    
    po object_getClass([Person class])
    Person
      
    po object_getClassName(self.person)
    "NSKVONotifying_Person"
      
    //说明:
    //    KVO实现了对象的isa的swizzling
    //    对象在进入KVO后isa指向为NSKVONotifying_XXX(动态类)
    //    NSKVONotifying_XXX  继承于  XXX,是KVO动态生成的XXX的子类
    

    3、KVO对methodlist的影响

    // imp 代表 函数实现的指针
    // 指针的的改变 说明 子类重写了方法(函数有了新的实现,需要新的空间)
    
    //结论:
    //    1、重写了setNickName方法(setter方法)
    //    2、添加了class、dealloc、_isKVOA方法
    

    4、KVO对ivarlist的影响

    //🈚️(无,就是单纯的继承,什么操作都没有)
    

    5、KVO的移除都做了什么

    - (void)dealloc
    {
      /* 
        断点在这里时:
        po object_getClass(self.person)
        输出:NSKVONotifying_Person
        说明:self.person 的 isa 指向 NSKVONotifying_Person
        */
        [self.person removeObserver:self forKeyPath:@"nickName"];
      /* 
        断点在这里时:
        po object_getClass(self.person)
        输出:Person
        说明:self.person 的 isa 指向 Person
        */
      /*
        此时仍有NSKVONotifying_Person类存在,没有消失
        原因:节省性能,作为缓存,空间换时间
        已存入  getobject_classList  表中
      */
        NSLog(@"VC 移除之后");
    }
    
    

    相关文章

      网友评论

          本文标题:iOS-KVO相关

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