面试中常见问题总结

OC 的动态性

OC是基于C语言的,C语言在编译阶段就会确定了调用具体的那个函数,OC它的底层是通过runtime 运行时机制来调用函数,在OC中成为消息发送,具体来说就是在编译阶段OC可以调用任何函数,即时这个函数是没有实现的,只有在运行的时候才会根据isa指针和已经注册的方法列表中找到 IMP(函数指针)真正要调用的那个函数,我们平时写的OC代码,在运行的时候也都是转换成了runtime 的方式进行的,不管是什么方法本质都是发送一个消息

runtime

  • 消息机制原理: 根据方法编号列表 SEL 去映射表中查找对应方法的实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Person *p = [Person allo] init];

// 底层的写法
// alloc
Person *p = objc_msgSend(objc_getClass("Person"), sel_registerName("alloc"),);

// init
p = objc_msgSend(p, sel-registerName("init"));

// 调用对象方法本质就是:让对象发消息
objc_msgSend(p, @selector(eat));

// 调用类方法本质: 让类发消息
objc_msgSend([Person class], @selector(run:), 20);

// alloc init 也可以理解为:

id objc = objc_msgSend([NSObject class], @selector(alloc));

objc = objc_msgSend(objc, @selector(alloc));
  • runtime 消息机制调用流程

    每个对象内部都有一个isa指针,指向他自己真实的类来确定调用的是那个类的方法; 每个方法sel都有一个IMP指针(指向现实函数的指针),不管是对象方法还是类方法,都会有一个方法列表

    1. 发消息时根据对象内部的isa 指针去该类所对应的方法类表中查找方法,如果没有去父类中查找
    2. 注册每个方法编号,为了以后快速查找调用
    3. 根据方法编号查找对应方法
    4. 通过isa指针和IMP指针确定最终调用的函数
  • runtime 常见使用

    1. 运行是动态添加方法
    2. 给分类添加属性,(关联属性)
    3. 字典转模型中从先遍历模型属性再从字典中查找相关值实现模型属性赋值
    4. NSCoding 自动归档解档时对象属性过多时 encodeWithCoder:initWithCoder 两个方法实现

Runloop

  • 概念

    NSRunloop 是封装 CFRunloopRef 一套纯C的函数,

  • 作用

    1. 保持程序持续运行
    2. 处理App中各种事件,具体的有:toches事件, NSTimer 事件, Selector 事件, PerformSelector 事件,source 源等等
    3. 有事件触发时就运行,没有就休眠
    4. 渲染app 中唯一一个主线程上所有UI界面的更新
  • 使用和要点

    1. Runloop 在程序入口 main 函数中就会开启,并且开启的是主线程不会销毁除非退出程序,保证了程序一直运行并能响应事件和交互
    2. 每一条线程都是唯一对应一个Runloop
    3. 主线程不需要创建,因为在程序启动的时候就已经创建了,而子线程的需要手动创建 直接获取currentRunloop 方式(本质是使用了懒加载)
    4. Runloop 对象是利用字典进行存储,因为一一对应,所有key 和value 就是线程和 他所对应的runloop

    5. Runloop 中 NSDefaultRunLoopMode 一般情况下主线程运行的 Mode

    6. UIInitializationRunLoopMode 初始化mode,通常不用
    7. UITrackingRunLoopMode 界面跟踪, 用于scrollView 追踪触摸滑动,保证界面不受其他mode影响
    8. GSEventReceiveRunLoopMode 用于接收系统事件内部 mode, 通常不用

    9. NSRunLoopCommonModes, 并不是某种具体的 mode ,开发中使用最多,他是一个组合 mode,组合了 NSDefaultRunLoopMode和 NSRunLoopCommonModes

  • Runloop 相关类

    1. Source 事件源/输入源可以理解为 激发源
    2. Timer 基于事件触发,CADisplayLink,NSTimer 都是加到了 Runloop,受 Mode 的影响,GCD 定时器不受Runloop 影响
    3. Observer 相当于runloop 循环中的监听器,时刻监听当前Runloop的运行状态
    4. 休眠没事情做的时候可以停下来,处于休眠状态
  • 使用场景

    1. 创建NSTimer
    2. PersforSelector方法
    3. 常驻线程:(创建后处于等待状态,有事件时响应事件)
      • 实现后台收集用户停留时间或者是某个按钮的点击次数,如果使用主线程会不方便,使用runloop解决
    4. AutoreleasePool 自动释放池
    5. UI更新