iOS小知识之底层问题探索

883 阅读2分钟

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。

1.主类方法和分类方法的调用顺序?

1.1 普通方法

普通方法,包括initialize,优先分类中的方法调用
因为分类的方法是在类realize之后attach进去的,所以插在前面

1.2 load方法

load方法,优先主类,然后分类
因为类的初始化,优先主类,读取ro。然后分类初始化,读取rwe

1.3 多分类

多个分类之间,看文件的编译顺序。load方法,先编译的分类先执行。同名方法,最后编译的分类中的方法会执行

2. load、initialize、cxx的调用顺序?

load方法和cxx函数,在程序启动时自动调用,调用顺序:load -> cxx -> main
对于相同镜像文件,load方法一定在cxx函数之前
不同镜像文件的调用顺序:系统库优先 -> 动态库 -> 主程序 initialize方法,属于懒加载方法,在对象首次消息发送时调用
objc中的cxx函数,它会在_objc_init函数中,调用static_init函数,执行C++静态构造函数

image-10.png

3. 能否在运行时对编译后的类添加实例变量?

不能,因为编译后的实例变量存储在ro中,一旦编译完成,内存结构就完全确定了,无法修改。可以对编译后的类添加属性和方法。

4.能否对运行时创建的类添加实例变量?

可以对运行时创建的类添加实例变量,使用objc_allocateClassPair创建类,只要在objc_registerClassPair注册之前,可添加实例变量。一旦注册后,无法添加实例变量。
使用class_addIvar添加实例变量,添加钱进行flags & RW_CONSTRUCTING的条件判断

BOOL class_addIvar(Class cls, const char *name, size_t size, uint8_t alignment, const char *type) 
{
    ... 
    
    // No class variables 
    if (cls->isMetaClass()) {
        return NO; 
    }
    
    // Can only add ivars to in-construction classes. 
    if (!(cls->data()->flags & RW_CONSTRUCTING)) { 
        return NO; 
    }
    
    ... 
}

调用objc_registerClassPair函数,会对flags进行标记

void objc_registerClassPair(Class cls)
{ 
    ...
    
    // Clear "under construction" bit, set "done constructing" bit 
    cls->ISA()->changeInfo(RW_CONSTRUCTED, RW_CONSTRUCTING | RW_REALIZING); 
    cls->changeInfo(RW_CONSTRUCTED, RW_CONSTRUCTING | RW_REALIZING);
    
    ... 
}

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

  • 1.通过SideTable找到我们的weak_table
  • 2.weak_table根据referent找到或者创建weak_entry_t
  • 3.然后append_referrer(entry, referrer)将我的新弱引用的对象加进去entry
  • 4.最后weak_entry_insertentry加入到我们的weak_table

image-11.png