IMG_5672.JPG

Q1:load方法在什么时候调用?

  • load方法通过load_iamges调用,底层有两种表,一张load方法表,一张分类的load方法表。底层调用时,递归对单个类的load方法进行搜集,结果放入上述两张表中。类的load方法优先于分类的load方法。
    • dyld->loadImages->prepare_load_methods->schedule_class_load->add_class_to_loadable_list->getLoadMethod->key(cls)、value(method)形式搜集到一张表中,类搜集完毕进行分类搜集,完毕后调用call_load_methods()
    • call_load_methods,遍历load表和load分类表,向类发送消息(*load_method)(cls, @selector(load))
  • 如果类之间不存在继承关系,那么优先加载的类,其load方法会先执行,按照编译顺序进行调用。
  • 如果类之间存在继承关系,那么底层调用时,会先去查找父类的load方法,将其加入到load方法表中,父类的load方法调用优先于类的load方法调用。
  • load方法调用顺序 父类 -> 类 -> 分类,Categories方法则调用分类方法
  • 流程图

音视频设备相关流程图.jpg

Q2:initialize和load方法,谁先调用?

  • 3个自调用方法,load方法、initialize方法、C++构造方法
  • load方法先,initialize在第一次发送消息时调用即lookupImpOrForward时调用
  • C++构造函数方法调用时机,load方法后main方法前

    • 特殊,objc_init会自启,如果C++方法写在objc里,比如objc_init中的static_init中写入C++方法,会提前调用

      Q3:多个分类,调用顺序如何?

  • 多个分类时,调用顺序取决于编译顺序,Build Phases -> Compile Sources

    Q4:runtime是什么?

  • runtime是由C和C++、汇编实现的一套API,为OC语言加入了面向对象,运行时的功能

    • 运行时(Runtime)是指将数据类型的确定由编译推迟到运行时—-> rwe
      • 例如:extension - category的区别
      • AOP
  • 平时编写的OC代码,在程序运行过程中,其实最终会转换成Runtime的C语言代码,Runtime是Objective-C的幕后工作者
  • 应用:

    • 方法交换
    • 实现给分类添加属性
    • 实现字典的模型和自动转换
    • aspect切面编程
    • JSPatch替换已有的OC方法

      Q5:能否动态添加实例变量?

      能否向编译后的得到的类中添加实例变量?

  • 不能。我们编译好的实例变量存储的位置在ro,一旦编译完成,内存结构就完全确定就无法修改。

    能否向运行时创建的类中添加实例变量?

  • 可以

    • 通过objc_allocateClassPair创建类簇,在调用objc_registerClassPair注册类之前添加都可以,指定class_addIvar
    • class_addIvar源码中,判断cls->info & CLS_CONSTRUCTING为false时,返回NO,添加不成功。
    • objc_registerClassPair则是更改了CLS_CONSTRUCTING值 ```cpp ///<< 添加实例变量 BOOL class_addIvar(Class cls, const char *name, size_t size,

      1. uint8_t alignment, const char *type)

      { bool result = YES;

      if (!cls) return NO; if (ISMETA(cls)) return NO; if (!(cls->info & CLS_CONSTRUCTING)) return NO; … }

///<< 注册类 void objc_registerClassPair(Class cls) { … // Clear “under construction” bit, set “done constructing” bit cls->info &= ~CLS_CONSTRUCTING; cls->ISA()->info &= ~CLS_CONSTRUCTING; cls->info |= CLS_CONSTRUCTED; cls->ISA()->info |= CLS_CONSTRUCTED;

  1. ///<< 将bucket插入到table表中
  2. NXHashInsertIfAbsent(class_hash, cls);

}

  1. <a name="q36V6"></a>
  2. ## Q6:以下代码运行结果?
  3. - CCTeacher继承于CCPerson,运行结果为CCTeacher - CCTeacher
  4. ```cpp
  5. @implementation CCTeacher
  6. - (instancetype)init
  7. {
  8. self = [super init];
  9. if (self) {
  10. NSLog(@"%@ - %@", [self class], [super class]);
  11. }
  12. return self;
  13. }
  14. @end

self

    • class底层源码为object_getClass(self),其中self为Teacher,方法包含两个隐藏参数
      • id self 和 sel _cmd,self是参数,传递进来的teacher
      • object_getClass获取对象的ISA,即类CCTeacher ```cpp
  • (Class)class { return object_getClass(self); }

Class object_getClass(id obj) { if (obj) return obj->getIsa(); else return Nil; }

  1. <a name="dwQwZ"></a>
  2. ### super
  3. - [super class] 中的消息接受者是?
  4. - clang对CCTeacher进行编译得到.cpp文件,[super class]是通过objc_msgSendSuper进行消息转发的,参数是__rw_objc_super类型和SEL,
  5. ```cpp
  6. static instancetype _I_CCTeacher_init(CCTeacher * self, SEL _cmd) {
  7. self = ((CCTeacher *(*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("CCTeacher"))}, sel_registerName("init"));
  8. if (self) {
  9. NSLog((NSString *)&__NSConstantStringImpl__var_folders_62_xk0q0gh50qsgfk5m4r5ynbb80000gn_T_CCTeacher_bbc596_mi_0, ((Class (*)(id, SEL))(void *)objc_msgSend)((id)self, sel_registerName("class")), ((Class (*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("CCTeacher"))}, sel_registerName("class")));
  10. }
  11. return self;
  12. }
  • super是编译器关键字,super_class is the first class to search,第一个被搜索,是父类

    1. /// Specifies the superclass of an instance.
    2. struct objc_super {
    3. /// Specifies an instance of a class.
    4. __unsafe_unretained _Nonnull id receiver;
    5. /// Specifies the particular superclass of the instance to message.
    6. #if !defined(__cplusplus) && !__OBJC2__
    7. /* For compatibility with old objc-runtime.h header */
    8. __unsafe_unretained _Nonnull Class class;
    9. #else
    10. __unsafe_unretained _Nonnull Class super_class;
    11. #endif
    12. /* super_class is the first class to search */
    13. };
    14. #endif
  • 静态编译时是objc_msgSendSuper,动态运行则变为objc_msgSendSuper2,objc_super结构体中的类也变为了父类

image.png

  • 总结
    • [self class],就是发送消息objc_msgSend,消息接受者是self,sel是class
    • [super class],是发送消息objc_msgSendSuper(经汇编转换调用_objc_msgSendSuper2)
      • id objc_msgSendSuper(struct objc_super *super, SEL op, …)第一个参数是objc_super,第二参数是SEL
      • objc_super中receiver是隐藏的参数,即传递的对象,cls则是当前对象的类的父类
      • objc_msgSendSuper2时,objc_super中的cls为当前类(类的父类的子类)
      • llvm源码
    • self = [super init] 通过父类init,使得子类self自带相关属性

image.png

Q7:内存平移问题

  • 如下代码都能打印出saySomething
    • saySomething方法在类的data中,只要有地址指向类,就可以在类中寻找方法
    • person对象通过isa(不纯的)找到类首地址,再进行平移到bits,取其中data中的methodList
    • kc指向类的首地址,再平移取methodList
  • 打印属性时,前者为nil,后者为何为LGPerson?
    • person是对象,开辟了内存空间,内存中存储了isa、成员变量信息,打印成员变量时根据内存存储打印,首地址 + offset
    • kc没有开辟内存,打印的是类本身,也是首地址 + offset

image.png
未命名文件 (11).jpg

  • kc调用打印原因,压栈,找上一个栈内容
  • x/4gx 中冒号前的是地址,后面的是值

image.png

参数压栈

  • 建立结构体,分别打印per、ks_s、person地址

image.png

  • viewDidLoad -> 包含两个隐藏参数{id self, SEL _cmd}
  • [super viewDidLoad] -> 结构体(objc, Class)
  • 验证上述打印ViewController情况

未命名文件 (12).jpg

  • 循环打印压栈信息

image.png

压栈时机

  1. 什么东西才会压栈 -> 临时变量压栈,包括参数
  2. [super viewDidLoad]编译为ObjcMsgSend,压入栈帧
  3. 打印ViewController -> 调用super viewDidLoad时,隐藏参数sel和viewController,传入的是ViewController

    Q8:OC是动态语言,未引用不一定没有用到,这个怎样排查

  • isa里面有个标志 表示是否被初始化 可以动态统计
  • magic:⽤于调试器判断当前对象是真的对象还是没有初始化的空间

    Q9:超出作用域的如何响应事件,比如点击事件

  • 重写hitTest方法

  • 重写pointInset方法

    Q10:类簇

  • 类簇是Foundation框架中广泛使用的设计模式->抽象工厂模式。类簇在公共抽象超类下对多个私有的具体子类进行分组。简化了面向对象框架的公共可见体系结构,而不会降低其功能丰富度。

  • 常见类簇:NSString、NSArray、NSDictionary

    • 以数组为例,不管创建的是可变还是不可变的数组,在alloc之后得到的类都是NSPlaceholderArray。而当我们init一个不可变数组之后,得到的是NSArray0;如果有且只有一个元素,那就是NSStringObjectArrayI; 有多个元素,叫NSArrayI; init一个可变数组,都是__NSArrayM;

      设计模式

  • 创建型模式

    • 单例模式
    • 工厂方法模式
    • 抽象工厂模式
  • 结构型模式
    • 代理模式
    • 类簇
    • 装饰模式
    • 享元模式
  • 行为型模式
    • 观察者模式
    • 命令模式