iOS开发面试攻略(KVO、KVC、多线程、锁、runloop、计时器)

KVO & KVC

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;
    }
}

使用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

多线程

进程和线程的区别

  • 进程:进程是指在系统中正在运行的一个应用程序,一个进
  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值