类的加载

在上一篇文章中,我们了解了objcdyld的关联,那么关联之后怎么对类进行加载的呢, 本篇将对类的加载进行探索和分析。

在上一篇文章中我们通过objc_init里面的_dyld_objc_notify_register 来到了_read_images

_read_images

我们点到_read_images里面去,把项目跑起来,然后打断点调试,看看走哪些代码

readClass读取自己研究的类

来到3597行的时候,我们在里面判断一下读取自己的类。

readClass

因为这里还会读取很多系统的类,而系统的类我们又不好操控,所以在readClass里面,我们写上这么一段代码,读取到我们自己的类,,让自己创建的类进来。

断点调试

来到我们自己类之后接着往下走。通过在判断条件里面打断点的方式,判断每个if下面有没有走。

for (EACH_HEADER)

接着来到realizeClassWithoutSwift实现当前的类,我们在自己研究的类这里打个断点,发现没有进来,这是啥情况,一般人又玩不进来了。 其实苹果也给了注释 Realize non-lazy classes (for +load methods and static instances) 大概意思是需要实现非懒加载的类才可以进去,用+load方法来实现。那么我们在LGPerson里面加一个load方法试试

image.png

加完之后我们再来运行一下看看进没进来

非懒加载类进到分类

果然进来了,从而得出加了load方法后让类提前实现成为非懒加载类,那么懒加载类在什么时候呢,我们把load方法注释来到realizeClassWithoutSwiftbt打个断点,然后打印一下堆栈

bt堆栈

在我们[LGPerson alloc];其实是进行了一次消息的发送_objc_msgSend_uncached,也就是当你要用到这个类的时候才会去实现这个类,这样大大减少了程序启动前的内存加载。

我们用一张PPT来总结下懒加载和非懒加载的区别:

懒加载和非懒加载区别

类的加载

明白了懒加载和非懒加载的区别之后,回到_read_images进来的断点里面,断点进来后我们接着进到realizeClassWithoutSwift里面去,这个方法也是本篇文章研究的重点,因为在类相关的判断里面都有realizeClass实现类的方法。

从而能看出来类就是在realizeClassWithoutSwift这个里面实现的,其实之前在lookupimp慢速查找的时候也提到过,那我们点进去看一下到底是怎么实现的

realizeClassWithoutSwift只研究本类

首先在开始前还是加一句只研究本类的代码, 排除其它类和元类,同时在自己类里面加了一些方法和属性,方便研究。

LGPerson.h

加好之后,我们回到realizeClassWithoutSwift,接下来再断点定到自己的类,接着往下看

读取data

在这个地方从我们的MachO里面读取到了我们的data,按照一定的格式转换成class_ro_t赋值给了临时变量ro

设置data

拿到ro之后判断是否为元类,显然不是,所以来到else里面,来到里面之后第一句代码rw = objc::zalloc(); 申请和开辟class_rw_t,此时rw还只是刚创建,是空的,然后set_roro放到了rw里面,再setDatarw给了LGPerson,设置完了之后LGPerson还是空的,接着往下走

确定继承链的关系

走到这两句就意味着把我们类信息确定好之后,再确定父类和元类,从LGPerson 类信息 -> 父类 -> 元类 就是为了确定继承链关系,实现完了之后就会来到isa的设置,完了之后就把整个继承链设置好了。

接着往下走,设置一些其它信息,比如setHasCxxDtor,设置Cxx,这就是为什么类里面有这么个Cxx的方法,这是系统默认设置的。

image.png

设置完了之后来到methodizeClass这句重点,点进去,然后在methodizeClass继续做一个判断,只研究当前自己的类,因为在realizeClassWithoutSwift递归了父类和元类,所以methodizeClass会进来父类和元类,写这个判断就是为了排除别的类。

methodizeClass

来到自己的类之后,把rwrorwe拿了出来

auto rw = cls->data();
auto ro = rw->ro();
auto rwe = rw->ext();

然后把baseMethods拿出来

method_list_t *list = ro->baseMethods();
    if (list) {
        prepareMethodLists(cls, &list, 1, YES, isBundleClass(cls));
        if (rwe) rwe->methods.attachLists(&list, 1);
    }

然后走到prepareMethodLists,看下里面做了什么操作

prepareMethodLists

看到下面有一句fixupMethodList,注释的意思是修正方法list,应该是对方法排序的意思,我们点进去看一下

fixupMethodList

果然,把这里把方法的名字进行一系列的处理之后开始sort 排序

SortBySELAddress

根据sel的名字来进行排序,排序前和排序后分别来打印一下

排序打印
注意,这里是根据名字的地址大小进行排序的,而不是文字的字符串大小排序的。

把顺序排好之后,来到rwe,我断点走完之后发现rwe始终为NULL,这是啥情况,这个地方让很多人百思不得其解,在后面我会为大家揭晓。我们先来看一下methodizeClass这个方法上面的注释Attach categories 附加分类,说明跟分类有关系,既然说到分类那就看看分类里面到底有些什么东西,可以通过Clang来看,我这里就直接搜索category_t来看

分类的本质

看到category_tclassMethodsprotocols等等,那么这里面的这些东西怎么加到我们的类里面去的呢,带着这个问题继续来探索:

我们在methodizeClass往下翻会看到分类相关的东西

分类加载的调用

1480行有一个attachToClass,非常好,我们点进去看到底是怎么加到类里面去的

attachToClass

老规矩,在前面还是加上只研究本类的判断, 看到category_list &list = it->second; ,然后在下面进行实现分类的判断调用了attachCategories, 如果if (flags & ATTACH_CLASS_AND_METACLASS) 满足,则说明类和分类都实现了,如果不满足,则说明主类没有实现load方法,而分类实现了load方法需要进行加载了,就会来到else下面。

我们点到attachCategories里面去看看

attachCategories

来到这里面之后我们意外的发现 auto rwe = cls->data()->extAllocIfNeeded();rwe原来就在这里进行创建或者获取,看来要对本类进行添加分类信息的时候才会处理这个rwe。那么还有哪些地方有用到吗,我们全局搜索

全局搜索extAllocIfNeeded

通过全局搜索能够看出rwe除了在添加分类以外,还在除了系统外的addMethodaddProtocoladdProperty这些地方用到过,说白了正如WWDC里面所说的只有对原始的内存进行处理和修改的时候我们才会用到rwe

明白了rwe之后,我们再回attachCategories接着打一个断点进入LGPerson当前要研究的类,然后开始遍历cats_count

遍历cats_count

可以看到这里来到的是LGPerson的分类LGA,然后拿到分类的method_list

倒序插入

拿到分类的方法列表之后,判断mcount是否等于ATTACH_BUFSIZ,表示最大修改次数为64次, 如果不等于则mlists[64 - 1] = mlist进行倒序插入

if (mcount > 0) {
        prepareMethodLists(cls, mlists + ATTACH_BUFSIZ - mcount, mcount, NO, fromBundle);
        rwe->methods.attachLists(mlists + ATTACH_BUFSIZ - mcount, mcount);
        if (flags & ATTACH_EXISTING) flushCaches(cls);
    }

倒序插入完了之后会来到

if (mcount > 0) {
        prepareMethodLists(cls, mlists + ATTACH_BUFSIZ - mcount, mcount, NO, fromBundle);
        rwe->methods.attachLists(mlists + ATTACH_BUFSIZ - mcount, mcount);
        if (flags & ATTACH_EXISTING) flushCaches(cls);
    }

到这一步,我们数据已经准备好了,那么到底是怎么加载到我们类里面去的呢,还有在什么时机加进去的,这将在下篇文章进行展开分析。 本来这篇文章只讲类的加载的,结果摸到分类来了,也算是提前给下一篇文章做个预告

未完待续。。。。

iOS 底层原理 文章汇总

你可能感兴趣的:(类的加载)