【面试-1】Runtime Asssociate方法关联的对象,需要在dealloc中释放?
上篇文章的_object_remove_assocations方法有介绍过关联对象的释放问题,目测看是不需要不需要我们手动移除,在dealloc中自动释放
查看dealloc源码
- dealloc
- _objc_rootDealloc
- rootDealloc
- object_dispose
- objc_destructInstance
【面试-2】方法的调用顺序
如果同名方法是普通方法,包括initialize -- 先调用分类方法
-
因为
分类的方法是在类realize之后 attach进去的,插在类的方法的前面,所以优先调用分类的方法(注意:不是分类覆盖主类!!) -
initialize法什么时候调用?initialize方法也是主动调用,即第一次消息时调用,为了不影响整个load,可以将需要提前加载的数据写到initialize中
如果同名方法是load方法
- 先
主类load,后分类load(分类之间,看编译的顺序)
【经典面试-3】 [self class]和[super class]的区别以及原理分析
示例
打印结果都是LGTeacher why?
分析:
- 查看class方法
进入object_getClass
都是返回isa
- 获取super和self的本质
打开终端生成.cpp文件,进入文件查看本质
本质上两个方法的接受这都是self,
-
self调用class,转换为c++中是通过
objc_msgSend发送class消息。 -
super调用class,在c++中是通过
objc_msgSendSuper发送class消息。 -
objc_msgSendSuper的第一个参数是一个结构体,而objc_msgSend第一个参数是id类型
进入objc_msgSendSuper方法,查看源码
搜索不到
objc_msgSendSuper实现方法,在定义中可以看到objc_super结构体
搜索objc_super,进入
由上图可以知道打印的原因:
-
super是一个关键字,不代表父类,它是指定方法从父类开始查找,根据isa走位图,最后也是找到NSObject中。super时,编译期间调用的是objc_msgSendSuper函数,也有两个默认参数,第一个是struct objc_super *类型,第二个是sel。而结构体中第一个属性接受者,也是self。这样调用class返回的也是self的isa。 -
self调用class方法时,先在当前类中查找方法,找不到就去父类中找。最后到NSObject中。self调用时,编译期间调用的是objc_msgSend函数,函数中有两个默认参数第一个是self,第二个是sel。调用class返回的是self的isa。
在NSLog前打个断点,既然汇编状态:
查找运行时调用的是objc_msgSendSuper2,我们在objc源码中看看它的实现
- 注释中解释了,
objc_super的第二个属性是当前类 - 汇编代码还是获取
super class进行查找,和我们上面的分析还是一样的。
【面试-4】内存平移问题
示例
打印结果:
分析:
person的isa指向类LGPerson即person的首地址 指向 LGPerson的首地址,我们可以通过LGPerson的内存平移找到cache,在cache中查找方法
[(__bridge id)kc saySomething]中的kc是来自于LGPerson这个类,然后有一个指针kc,将其指向LGPerson的首地址
所以,person是指向LGPerson类的结构,kc也是指向LGPerson类的结构,然后都是在LGPerson中的methodList中查找方法
参考《内存地址解析》
- kc为一个 指针地址,是存在
栈中的,栈是一个先进后出的结构,参数传入就是一个不断压栈的过程(从高向低位),验证
通过这种方式,我们可以清楚的看到栈中的内存情况,也证明了结构体属性是“反向”入栈的规则。
所以,当用桥接这种地址调用内存的过程,就是内存平移的过程。
注意:
当此方法调用方法时,为8字节,如果此时的地址少于8字节,就回导致崩溃,所以此类方法调用必须要保持字节对齐
例如: