Runloop和KVO相关面试题

187 阅读4分钟

Runloop

1.app如何接收到触摸事件的

回答这个问题前请认真阅读一下 iOS触摸事件全家桶

image.png 通过上图可以看出整个流程就是 我们app启动默认会通过machPort监听端口的方式 来接受IOHIDEvent 来接收和处理触摸事件.

2.为什么只有主线程的runloop是开启的

  • mian()函数中调用UIApplicationMain,这里会创建一个主线程,用于UI处理,为了让程序可以一直运行并接收事件,所以在主线程中开启一个runloop,让主线程常驻.

3.为什么只在主线程刷新UI

  • UIKit是一个线程不安全的类,UI操作涉及到渲染访问各种View对象的属性,如果异步操作下会存在读写问题,而为其加锁则会耗费大量资源并拖慢运行速度。
  • 另一方面因为整个程序的起点UIApplication是在主线程进行初始化,所有的用户事件都是在主线程上进行传递(如点击、拖动),所以view只能在主线程上才能对事件进行响应。
  • 而在渲染方面由于图像的渲染需要以60帧的刷新率在屏幕上同时更新,在非主线程异步化的情况下无法确定这个处理过程能够实现同步更新。

4.PerformSelectorrunloop的关系

  • 当调用NSObectperformSelector:相关的时候,内部会创建一个timer定时器添加到当前线程的runloop中,如果当前线程没有启动runloop,则该方法不会被调用.
  • 开发中遇到最多的问题就是这个performSelector: 导致对象的延迟释放,这里开发过程中注意一下,可以用单次的NSTimer替代.

5.如何使线程保活

  • 想要线程保活的话就开启该线程的runloop即可,注意:在NSThread执行的方法中添加while(true){},这样是模拟runloop的运行原理,结合GCD的信号量,在{}代码块中处理任务. image.png

KVO

1.实现原理

  • 通过runtime派生子类的方式 复写相关需要KVO监听的属性,在该属性setter之前和之后调用NSObject的监听方法,这样KVO就实现了属性变换前后的回调.
  • KVO派生的子类具体格式应该是:NSKVONotifying_+类名的类 eg: NSKVONotifying_Person
  • NSKVONotifying_Person内部都重写了
    • class 重写这个方法,是为了伪装苹果自动为我们生成的中间类
    • dealloc 应该是处理对象销毁之前的一些收尾工作
    • _isKVOA 告诉系统使用了kvo
    • setName

2.如何手动关闭kvo

  • 被观察的对象复写如下方法 返回NO即可关闭KVO + (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key {return NO;}
  • 如果关闭后还想触发 KVO的话 修改需要手动调用在变量setter的前后 主动调用 willChangeValueForKey:didChangeValueForKey:

3.通过KVC修改属性会触发KVO么

会的, 因为调用了set方法

4.哪些情况下使用kvo会崩溃,怎么防护崩溃

  • 添加和移出不是成对出现且存在多线程添加KVO的情况,经常遇到的crash是移出 - 内存dealloc的时候 或者对象销毁前没有正确移出Observer
  • 1.注意移出对象 匹配
  • 2.野指针问题,一定要在对象销毁前移出观察者
  • 3.使用第三方库BlockKit添加KVO,blockkit内部会自动移除Observer避免crash.

5.kvo的优缺点

优点:

  • 方便两个对象间同步状态(keypath)更加方便,一般都是在A类要观察B类的属性的变化.
  • 非侵入式的得到某内部对象的状态改变并作出响应.(就是在不改变原来对象类的代码情况下即可做出对该对象的状态变化进行监听)
  • 可以嵌入更改前后的两个时机的状态. - 可以通过Keypaths对嵌套对象的监听. 缺点:
  • 需要手动移除观察者,不移除容易造成crash.
  • 注册和移出成对匹配出现.
  • keypath参数的类型String, 如果对象的成员变量被重构而变化字符串不会被编译器识别而报错.
  • 实现观察的方式是复写NSObjec的相关KVO的方法,应该更加面向protocol的方式会更好.