iOS开发底层之常见问题解答 - 15

Posted iOS_developer_zhong

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了iOS开发底层之常见问题解答 - 15相关的知识,希望对你有一定的参考价值。

1. load与c++构造函数调用顺序

  1. load是在dyld的回调方法load_images中调用。
  2. c++构造函数在同一个image来说load方法会先调用. (并不是绝对)
  3. image内部是先加载所有类的load,在加载分类的load,最后加载c++构造函数,image内部的顺序默认是按照xcode中的 Compile Sources的先后顺序。 如果image有依赖库,依赖库的load先调用。

2. runtime是什么?

  1. runtime是一套由C实现的一套API,OC语言所拥有的运行时机制,就是由于runtime的存在。 它才是幕后工作者
  2. runtime将对象类型的确定由编译时推迟到了运行时。

3. initialize调用顺序?

  1. initialize是在第一次发送消息的时候进行的调用, load方法是在loadImage方法中调用的。 load会先被调用。
  2. initialize是在lookupimporforward 慢速消息查找的时候调用。
  3. load > ==c++==构造函数 > initialize 的三者的调用顺序

4. 同名分类方法的调用顺序?

调用顺序和分类中load方法的数量相关:

  1. 没有或者只有一个load方法调用的情况:整个方法列表是一个一维数组,最后编译的同类方法名会放在最前面, 二分查找方法,一直往前查找,所以会找到最后编译的这个同类方法。
  2. 有多个load方法的调用的情况:整个方法列表为一个二维数组, 后编译的分类会在数组前面, 查找顺序为从前往后。
  3. 总结下调用的方法最终就是找到最后编译的分类的同名方法。

5. 分类和扩展的区别?

1. 分类 : category

  1. 分类是为了给类添加新方法。
  2. 分类不能添加属性, 但可以通过runtime添加属性,会生成变量的setter,getter方法申明, 不会产生带下划线的成员变量。

2. 扩展: extension

  1. 匿名的分类 (无名称),我们平常开发新建一个类,
@interface ZGRObject : NSObject {

}
  1. 可以添加方法,添加属性, 都是私有方法, 私有属性。

3. 两者对比

  1. 合并到类的时机: extension在编译的时候就已经把内容合并到类中, 而category是在运行时,才合并到类中。
  2. extension 可以添加方法和属性, 但方法不能像分类一样拥有独立的实现部分,只能是私有的。
  3. category也可以添加方法,并且是独立的,虽然不能添加成员变量,但可以通过关联对象间接实现。

6. 方法的本质 (发送消息流程)

消息转发机制如下:

  1. 快速消息查找 (objc_msgsend),查找自己的cache方法列表。
  2. 慢速消息查找, 递归自己的父类,找不到,就会进行父类的慢速消息查找,直到父类为nil为止。
  3. 动态方法解析,会先调用resolveClassMethod,然后在调用resolveInstanceMethod,没有找到就继续走下面步骤。
  4. 消息快速转发 == forwardingTargetForSelect== ,可以让指定消息指定者。
  5. 消息慢速转发(methodSignatureForSelector & forwardInvocation),如果这个方法也没有找到消息处理者, 在执行methodSignatureForSelector的时候,就会再次调用一次慢速消息查找, 但这次不进行消息转发了。
  6. 如果到最后没有找到方法,就会走 doesNotRecognizeSelector报错。

7. sel与imp两者的关系是什么?

sel 是方法编号, imp是方法实现指针, 找imp的过程,就是找函数的过程。

8. 能否向编译后的得到的类中增加实例变量?能否向运行时创建的类中添加实例变量?

  1. 不能向编译后的类中,增加实例变量, 因为编译后的实例变量是存储在 ro中, 前面的博客有说到 ro是不能修改的。

  2. 这个取决于创建的类有没有注册到内存中,没有的话,是可以增加实例变量的。 反之则不行。

9. [Self class] 和 [super class]的区别和原理分析

测试代码:

@implementation zgrObject

-(instancetype)init {
    
    self = [super init];
    if (self) {
        NSLog(@"---%@----%@------",[self class], [super class]);
    }
    return  self;
}
@end

打印结果如下: —zgrObject----zgrObject------

  1. [self class] 就是发送消息objc_msgSend ,消息接收者是self, 方法编号是class
  2. [super class]本质就是objc_msgSend(实际上是调用objc_msgSendSuper2),消息的接受者还是self, 方法编号是classobjc_msgSendSuperobjc_super构造是receiver:self superClass:superClass, 而 objc_msgSendSuper2objc_super构造是 receiver:Self, superClass:currentClass

10. Runtime是如何实现weak的,为什么可以⾃动置nil?

  1. 首先我们要了解下底层结构为 SideTable 包含 Weak_table 包含 weak_entry_t
  2. 通过SideTable找到我们的weak_table
  3. weak_table根据对象索引找到或创建新的weak_entry_t
  4. 然后append_refereer(entry, referrer)将我们新的弱引用的对象加进去weak
  5. 最后weak_entry_insertentry加入我们的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的主要内容,如果未能解决你的问题,请参考以下文章

iOS开发底层之常见问题解答 - 15

iOS开发之结构体底层探索

iOS开发底层之alloc原理初探

iOS开发底层之消息的快速与慢速转发 - 11

iOS开发底层之消息的快速与慢速转发 - 11

iOS开发底层之NSObject-alloc源码分析-02