洋仔的博客 洋仔的博客
首页
  • 个人心法总结

    • 价值心法
  • 技术文档
  • GitHub技巧
  • Nodejs
  • 博客搭建
  • iOS基础知识
  • 前端
  • 学习
  • 面试
  • 心情杂货
  • 实用技巧
  • 投资体系
  • 毛选
  • 友情链接
关于
收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

洋仔

奋斗的小青年
首页
  • 个人心法总结

    • 价值心法
  • 技术文档
  • GitHub技巧
  • Nodejs
  • 博客搭建
  • iOS基础知识
  • 前端
  • 学习
  • 面试
  • 心情杂货
  • 实用技巧
  • 投资体系
  • 毛选
  • 友情链接
关于
收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • 技术文档

  • GitHub技巧

  • Nodejs

  • 博客搭建

  • iOS基础知识

    • iOS底层相关

    • Runloop系列

    • Runtime系列

    • 内存管理系列

    • Block系列

    • 线程系列

    • KVC跟KVO系列以及通知中心

      • 通知中心系统
      • KVC跟KVO
        • KVO的优缺点
          • 优点
          • 缺点
        • 怎么手动触发KVO
        • 给KVO添加筛选条件
        • 使用KVC修改会触发KVO吗?
        • 直接修改成员变量会触发KVO吗?
        • KVO的崩溃与防护
        • setValue:forKey:的实现
        • valueForKey:的实现
    • UI系列

    • 离屏渲染系列

    • 组件化系列跟架构

    • OC跟webview交互系列

    • 持久化系列

    • APP编译系列

    • APP性能优化系列

    • cocoapods系列

    • swift系列

    • Git系列

    • 网络相关

    • 三方库系列

    • 系统原理

    • 总结系列

    • 算法系列

    • 数据结构系列

  • 前端

  • 技术
  • iOS基础知识
  • KVC跟KVO系列以及通知中心
洋仔
2023-09-27
目录

KVC跟KVO

# KVO用法和底层原理

  • 使用方法:添加观察者,然后怎样实现监听的代理
  • KVO底层使用了 isa-swizling的技术.
  • OC中每个对象/类都有isa指针, isa 表示这个对象是哪个类的对象.
  • 当给对象的某个属性注册了一个 observer,系统会创建一个新的中间类(intermediate class)继承原来的class,把该对象的isa指针指向中间类。
  • 然后中间类会重写setter方法,调用setter之前调用willChangeValueForKey, 调用setter之后调用didChangeValueForKey,以此通知所有观察者值发生更改。
  • 重写了 -class 方法,企图欺骗我们这个类没有变,就是原本那个类。

# KVO的优缺点

# 优点

1、可以方便快捷的实现两个对象的关联同步,例如view & model

2、能够观察到新值和旧值的变化

3、可以方便的观察到嵌套类型的数据变化

# 缺点

1、观察对象通过string类型设置,如果写错或者变量名改变,编译时可以通过但是运行时会发生crash

2、观察多个值需要在代理方法中多个if判断

3、忘记移除观察者或重复移除观察者会导致crash

# 怎么手动触发KVO

  • KVO机制是通过willChangeValueForKey:和didChangeValueForKey:两个方法触发的。
  • 在观察对象变化前调用willChangeValueForKey:
  • 在观察对象变化后调用didChangeValueForKey:
  • 所以只需要在变更观察值前后手动调用即可。

# 给KVO添加筛选条件

  • 重写automaticallyNotifiesObserversForKey,需要筛选的key返回NO。
  • setter里添加判断后手动触发KVO
  • (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key { if ([key isEqualToString:@"age"]) {

      return NO;
    

    } return [super automaticallyNotifiesObserversForKey:key]; } ​

  • (void)setAge:(NSInteger)age { if (age >= 18) {

      [self willChangeValueForKey:@"age"];
      _age = age;
      [self didChangeValueForKey:@"age"];
    

    }else {

      _age = age;
    

    } }

    
    
    1

# 使用KVC修改会触发KVO吗?

会,只要accessInstanceVariablesDirectly返回YES,通过KVC修改成员变量的值会触发KVO。 这说明KVC内部调用了willChangeValueForKey:方法和didChangeValueForKey:方法

# 直接修改成员变量会触发KVO吗?

不会

# KVO的崩溃与防护

崩溃原因:

  • KVO 添加次数和移除次数不匹配,大部分是移除多于注册。
  • 被观察者dealloc时仍然注册着 KVO,导致崩溃。
  • 添加了观察者,但未实现 observeValueForKeyPath:ofObject:change:context: 。 防护方案1:
  • 直接使用facebook开源框架KVOController 防护方案2:
  • 自定义一个哈希表,记录观察者和观察对象的关系。
  • 使用fishhook替换 addObserver:forKeyPath:options:context:,在添加前先判断是否已经存在相同观察者,不存在才添加,避免重复触发造成bug。
  • 使用fishhook替换removeObserver:forKeyPath:和removeObserver:forKeyPath:context,移除之前判断是否存在对应关系,如果存在才释放。
  • 使用fishhook替换dealloc,执行dealloc前判断是否存在未移除的观察者,存在的话先移除。

# KVC底层原理

# setValue:forKey:的实现

  • 查找setKey:方法和_setKey:方法,只要找到就直接传递参数,调用方法;
  • 如果没有找到setKey:和_setKey:方法,查看accessInstanceVariablesDirectly方法的返回值,如果返回NO(不允许直接访问成员变量),调用setValue:forUndefineKey:并抛出异常NSUnknownKeyException;
  • 如果accessInstanceVariablesDirectly方法返回YES(可以访问其成员变量),就按照顺序依次查找 _key、_isKey、key、isKey 这四个成员变量,如果查找到了就直接赋值;如果没有查到,调用setValue:forUndefineKey:并抛出异常NSUnknownKeyException。

# valueForKey:的实现

  • 按照getKey,key,isKey的顺序查找方法,只要找到就直接调用;
  • 如果没有找到,accessInstanceVariablesDirectly返回YES(可以访问其成员变量),按照顺序依次查找_key、_isKey、key、isKey 这四个成员变量,找到就取值;如果没有找到成员变量,调用valueforUndefineKey并抛出异常NSUnknownKeyException。
  • accessInstanceVariablesDirectly返回NO(不允许直接访问成员变量),那么会调用valueforUndefineKey:方法,并抛出异常NSUnknownKeyException;
编辑 (opens new window)
上次更新: 2024/10/23, 23:26:17
通知中心系统
UI系列

← 通知中心系统 UI系列→

最近更新
01
数组
10-25
02
数组双指针系列之对撞指针
10-25
03
数组双指针系列之快慢指针
10-25
更多文章>
Theme by Vdoing | Copyright © 2019-2024 Evan Xu | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式