iOS开发底层之常见问题解答 - 15
Posted iOS_developer_zhong
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了iOS开发底层之常见问题解答 - 15相关的知识,希望对你有一定的参考价值。
文章目录
1. load与c++构造函数调用顺序
- load是在dyld的回调方法load_images中调用。
- c++构造函数在同一个image来说load方法会先调用. (并不是绝对)
- image内部是先加载所有类的load,在加载分类的load,最后加载c++构造函数,image内部的顺序默认是按照xcode中的 Compile Sources的先后顺序。 如果image有依赖库,依赖库的load先调用。
2. runtime是什么?
- runtime是一套由C实现的一套API,OC语言所拥有的运行时机制,就是由于runtime的存在。 它才是幕后工作者
- runtime将对象类型的确定由编译时推迟到了运行时。
3. initialize调用顺序?
- initialize是在第一次发送消息的时候进行的调用, load方法是在loadImage方法中调用的。 load会先被调用。
- initialize是在lookupimporforward 慢速消息查找的时候调用。
- load > ==c++==构造函数 > initialize 的三者的调用顺序
4. 同名分类方法的调用顺序?
调用顺序和分类中load方法的数量相关:
- 没有或者只有一个load方法调用的情况:整个方法列表是一个一维数组,最后编译的同类方法名会放在最前面, 二分查找方法,一直往前查找,所以会找到最后编译的这个同类方法。
- 有多个load方法的调用的情况:整个方法列表为一个二维数组, 后编译的分类会在数组前面, 查找顺序为从前往后。
- 总结下调用的方法最终就是找到最后编译的分类的同名方法。
5. 分类和扩展的区别?
1. 分类 : category
- 分类是为了给类添加新方法。
- 分类不能添加属性, 但可以通过runtime添加属性,会生成变量的setter,getter方法申明, 不会产生带下划线的成员变量。
2. 扩展: extension
- 匿名的分类 (无名称),我们平常开发新建一个类,
@interface ZGRObject : NSObject {
}
- 可以添加方法,添加属性, 都是私有方法, 私有属性。
3. 两者对比
- 合并到类的时机: extension在编译的时候就已经把内容合并到类中, 而category是在运行时,才合并到类中。
- extension 可以添加方法和属性, 但方法不能像分类一样拥有独立的实现部分,只能是私有的。
- category也可以添加方法,并且是独立的,虽然不能添加成员变量,但可以通过关联对象间接实现。
6. 方法的本质 (发送消息流程)
消息转发机制如下:
- 快速消息查找 (objc_msgsend),查找自己的cache方法列表。
- 慢速消息查找, 递归自己的父类,找不到,就会进行父类的慢速消息查找,直到父类为nil为止。
- 动态方法解析,会先调用resolveClassMethod,然后在调用resolveInstanceMethod,没有找到就继续走下面步骤。
- 消息快速转发 == forwardingTargetForSelect== ,可以让指定消息指定者。
- 消息慢速转发(methodSignatureForSelector & forwardInvocation),如果这个方法也没有找到消息处理者, 在执行methodSignatureForSelector的时候,就会再次调用一次慢速消息查找, 但这次不进行消息转发了。
- 如果到最后没有找到方法,就会走 doesNotRecognizeSelector报错。
7. sel与imp两者的关系是什么?
sel 是方法编号, imp是方法实现指针, 找imp的过程,就是找函数的过程。
8. 能否向编译后的得到的类中增加实例变量?能否向运行时创建的类中添加实例变量?
-
不能向编译后的类中,增加实例变量, 因为编译后的实例变量是存储在 ro中, 前面的博客有说到 ro是不能修改的。
-
这个取决于创建的类有没有注册到内存中,没有的话,是可以增加实例变量的。 反之则不行。
9. [Self class] 和 [super class]的区别和原理分析
测试代码:
@implementation zgrObject
-(instancetype)init {
self = [super init];
if (self) {
NSLog(@"---%@----%@------",[self class], [super class]);
}
return self;
}
@end
打印结果如下: —zgrObject----zgrObject------
- [self class] 就是发送消息objc_msgSend ,消息接收者是self, 方法编号是class。
- [super class]本质就是objc_msgSend(实际上是调用objc_msgSendSuper2),消息的接受者还是self, 方法编号是class 。 objc_msgSendSuper的objc_super构造是receiver:self superClass:superClass, 而 objc_msgSendSuper2的objc_super构造是 receiver:Self, superClass:currentClass。
10. Runtime是如何实现weak的,为什么可以⾃动置nil?
- 首先我们要了解下底层结构为 SideTable 包含 Weak_table 包含 weak_entry_t
- 通过SideTable找到我们的weak_table。
- weak_table根据对象索引找到或创建新的weak_entry_t。
- 然后append_refereer(entry, referrer)将我们新的弱引用的对象加进去weak。
- 最后weak_entry_insert把entry加入我们的weak_table。
why 可以自动置为nil?
这里就要谈下 类的 dealloc 方法了。
底层源码如下:
inline void
objc_object::rootDealloc()
{
if (isTaggedPointer()) return; // 小对象概念, 苹果为了更好的内存管理, 增加了这个对象, 这个对象是针对基础的长度比较短的string ,char对象。
if (fastpath(isa.nonpointer &&
!isa.weakly_referenced &&
!isa.has_assoc &&
#if ISA_HAS_CXX_DTOR_BIT
!isa.has_cxx_dtor &&
#else
!isa.getClass(false)->hasCxxDtor() &&
#endif
!isa.has_sidetable_rc))
{ assert(!sidetable_present());
free(this); }
else { // 真正的销毁方法。
object_dispose((id)this);
}
}
id object_dispose(id obj)
{
if (!obj) return nil;
objc_destructInstance(obj);
free(obj);
return nil;
}
void *objc_destructInstance(id obj)
{
if (obj) {
// Read all of the flags at once for performance.
bool cxx = obj->hasCxxDtor();
bool assoc = obj->hasAssociatedObjects();
// This order is important.
if (cxx) object_cxxDestruct(obj); // 释放c++函数
if (assoc) _object_remove_assocations(obj, /*deallocating*/true); // 释放关联对象
obj->clearDeallocating(); // 释放弱引用表 ,释放引用技术表。
}
return obj;
}
// 引用表与弱引用表的释放。
inline void objc_object::clearDeallocating()
{
if (slowpath(!isa.nonpointer)) {
// Slow path for raw pointer isa.
sidetable_clearDeallocating();
}
else if (slowpath(isa.weakly_referenced || isa.has_sidetable_rc)) {
// Slow path for non-pointer isa with weak refs and/or side table data.
clearDeallocating_slow();
}
assert(!sidetable_present());
}
以上是关于iOS开发底层之常见问题解答 - 15的主要内容,如果未能解决你的问题,请参考以下文章