zoukankan      html  css  js  c++  java
  • iOS KVO 常见错误

    一、KVO

      Key-Value Observing 键值用于检测对象的某些属性的实时变化情况并作出响应

    KVO实现

    /** KVO 实现 */
    self.womanPerson = [[WLPerson alloc] init];
    [self.womanPerson addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew context:nil];
    self.womanPerson.name = @"rose";
    
    - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
        /** 同一个类里可能有多个KVO */
        if (object == self.person && [keyPath isEqualToString:@"name"]) {
            WLPerson *person = object;
            NSLog(@"---->%@", person.name);
        } else if (object == self.womanPerson && [keyPath isEqualToString:@"name"]) {
            WLPerson *person = object;
            NSLog(@"---->%@", person.name);
        }
    }

    在为一个对象添加观察者之时,framework使用runtime动态创建了一个WLPerson类的子类NSKVONotifying_WLPerson,而为了不让外部知道这一行为,NSKVONotifying_WLPerson重写了-(void)class方法返回之前的类

    self.person = [[WLPerson alloc] init];
    NSLog(@"
    -添加监听者之前-.class-->%@  
                ---isa--->%s", NSStringFromClass(self.person.class), object_getClassName(self.person));
    object_getClass(self.person);
    
    [self.person addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew context:nil];
    NSLog(@"
    -添加监听者之后-.class-->%@  
                ---isa--->%s", NSStringFromClass(self.person.class), object_getClassName(self.person));
    
    [self.person removeObserver:self forKeyPath:@"name" context:nil];
    NSLog(@"
    -移除监听者之后-.class-->%@  
                ---isa--->%s", NSStringFromClass(self.person.class), object_getClassName(self.person));
    
    
    打印:
    -添加监听者之前-.class-->WLPerson  
                 ---isa--->WLPerson
    -添加监听者之后-.class-->WLPerson  
                 ---isa--->NSKVONotifying_WLPerson
    -移除监听者之后-.class-->WLPerson  
                 ---isa--->WLPerson

    可以看到在添加观察之后.class打印出来的类没有变化,使用object_getClassName()打印出来的类是NSKVONotifying_WLPerson ,因为这个object_getClass()返回的是这个对象的isa指针,isa指针指向的一定是这个对象所属的类。

    二、常见错误

    1、romove 观察者

    [self removeObserver:self forKeyPath:@"name" context:nil];
    *** Terminating app due to uncaught exception 'NSRangeException', reason: 'Cannot remove an observer <ViewController 0x121d099d0> for the key path "name" from <ViewController 0x121d099d0> because it is not registered as an observer.'
    
    原因:crash信息已经很清楚了,谁添加的观察者,谁移除

    2、重复移除观察者

    [self removeObserver:self forKeyPath:@"name" context:nil];
    [self removeObserver:self forKeyPath:@"name" context:nil];
    
    crash *** Terminating app due to uncaught exception 'NSRangeException', reason: 'Cannot remove an observer <ViewController 0x100306e50> for the key path "name" from <WLPerson 0x17400a740> because it is not registered as an observer.’
    
    解决:网上有很多方法,最简单的try catch 
        @try {
            [self.person removeObserver:self forKeyPath:@"name" context:nil];
            [self.person removeObserver:self forKeyPath:@"name" context:nil];
        } @catch (NSException *exception) {
            NSLog(@"重复移除");
        } @finally {
            
        }
    可能有人会问了,谁这么傻逼移除两次,其实在很多情况下确实写的移除一次,但是可能。。。。。比如SDPhotobrowser就存在这样的crash

    3、属性的值修改了的信息收到了,但是没有处理 方法-observeValueForKeyPath:ofObject:change:context:却没有实现,只要你注册了观察者,这个方法必须实现

    *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: '<ViewController: 0x10040a1e0>: An -observeValueForKeyPath:ofObject:change:context: message was received but not handled.
    Key path: name
    Observed object: <WLPerson: 0x170005d00>
    Change: {
        kind = 1;
        new = jack;
    }
    Context: 0x0

    4、添加和移除时,content上下文不一致

    [self.person removeObserver:self forKeyPath:@"name" context:@"随便给个"];
    *** Terminating app due to uncaught exception 'NSRangeException', reason: 'Cannot remove an observer <ViewController 0x100704960> for the key path "name" from <WLPerson 0x1740046f0> because it is not registered as an observer.’
    
    ps:一般传nil

    5、给局部变量添加观察者

    WLPerson *tempPerson = [[WLPerson alloc] init];
    [tempPerson addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew context:nil];
    tempPerson.name = @"jack”;
    *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'An instance 0x174000880 of class WLPerson was deallocated while key value observers were still registered with it. Current observation info: <NSKeyValueObservationInfo 0x174022800> (
    <NSKeyValueObservance 0x174045a00: Observer: 0x100307e70, Key path: name, Options: <New: YES, Old: NO, Prior: NO> Context: 0x0, Property: 0x174045be0>
    )'

    未完待续。。。

  • 相关阅读:
    界这么大,Python 也想去看看 -- pyecharts的安装和使用
    Mybatis开发笔记
    Spring入门笔记
    Maven基础
    树莓派的20个常用命令
    树莓派CPU、GPU、磁盘、内存、负载监控Python脚本
    在Winform开发框架中使用DevExpress的TreeList和TreeListLookupEdit控件
    ABP开发框架前后端开发系列---(14)基于Winform的ABP快速开发框架
    ABP开发框架前后端开发系列---(13)高级查询功能及界面的处理
    ABP开发框架前后端开发系列---(12)配置模块的管理
  • 原文地址:https://www.cnblogs.com/10-19-92/p/4955950.html
Copyright © 2011-2022 走看看