本文衔接
OC 底层探索 13
继续探索类的加载,我们已知通过
readClass()
读取了编译器写的类(or元类),同时给
cls
赋了 name 和把cls 插入到了类/元类的表中,此时cls不仅有了地址还有了 name。此时的类是什么样子的呢?我们读取一下 cls 的内存情况,见下图:
通过上面lldb调试,我们发现cls 的 bits 是0000,并无数据. 继续执行到
realizeClassWithoutSwift()
中:
此时其实 cls 的内存还没有分配完毕,初始化和赋值还未处理好,内存还没有完善。
那么
auto ro = (
const
class_ro_t *)cls->
data()
; 进行了 data 的读取,这里的 data 是读的什么呢?
--> cls 的内存情况虽未完备,但是它现在已经从编译器读出来了,是有其唯一地址的,我们试着读取下 cls 指针的内容:
显然,bits 是有值的。 即我们可通过 cls 的地址指针进行识别数据的。
问题
:cls 是什么时候 bits 有值的呢?
下面继续
_read_images()
的源码分析。
Fix up remapped classes 未进入跳过,协议和分类我们这里暂时不对其进行具体的分析;
一点点扩展:
非懒加载
的类才会走进下面3637行代码,直接跑工程并未进去,我们如何走进去呢 --> 给
MyPerson
类添加
+load()
方法:
load
方法的实现为何就会使 MyPerson 的方法提前加载呢? --> load 方法是在
load_images
时就会调用的 --> 若类都没有实现如何 load_images 呢 --> so 方法都提前了
realizeClassWithoutSwift
(cls,
nil
)
;// 在此之前,cls 还只是一个地址和名字,但是 data 数据信息还是macho中,还并未加载读取到cls的内存中。通过 realizeClassWithoutSwift() 实现,下面对此函数源码进行探究。
tip
:
我们之前已知,在消息的转发中
lookUpIMPAndForward
流程中,类必须是已经实现的,若没有实现则必须
realizeClassWithoutSwift()
实现。这里就可知了原因,类必须是已经存在完备的,若是类没有实现,我们无法调其中任何方法的,alloc init 自然也是无法操作实例化出来
。
那么
懒加载
呢?我们注释掉
+load{}
方法,run,打印堆栈信息:
我们在
main.m
中调用了
MyPerson
*objc2 = [MyPerson
alloc
]
; // 懒加载 - 敌不动我不动,敌动我动。
懒加载的意义
:若没有懒加载,那么我们所有的类的实现 全部处理(代码实现、方法排序、临时变量等)有大量的工作要做,这些都要在 main 函数前处理完,那么会造成 main 函数的启动很慢;懒加载使我们不调用的类不先加载,节省了内存,提高了性能。<-- 这里也验证了为何 load 方法能不写就不写的原因。
下面进行进入
realizeClassWithoutSwift()
流程分析。
一、类的加载
realizeClassWithoutSwift()
源码分析:
1 /***********************************************************************
2 * realizeClassWithoutSwift
3 * Performs first-time initialization on class cls,
4 * including allocating its read-write data.
5 * Does not perform any Swift-side initialization.
6 * Returns the real class structure for the class.
7 * Locking: runtimeLock must be write-locked by the caller
8 **********************************************************************/
9 static Class realizeClassWithoutSwift(Class cls, Class previously)
10 {
11 runtimeLock.assertLocked();
13 class_rw_t *rw;
14 Class supercls;
15 Class metacls;
17 if (!cls) return nil;
18 if (cls->isRealized()) return cls;
19 ASSERT(cls == remapClass(cls));
21 // fixme verify class is not in an un-dlopened part of the shared cache?
23 auto ro = (const class_ro_t *)cls->data();
24 auto isMeta = ro->flags & RO_META;// 元类判断
25 if (ro->flags & RO_FUTURE) {
26 // This was a future class. rw data is already allocated.
27 rw = cls->data();
28 ro = cls->data()->ro();
29 ASSERT(!isMeta);
30 cls->changeInfo(RW_REALIZED|RW_REALIZING, RW_FUTURE);
31 } else {
32 // Normal class. Allocate writeable class data.
33 rw = objc::zalloc<class_rw_t>();// 开辟空间
34 rw->set_ro(ro);// ro 数据赋值给 rw
35 rw->flags = RW_REALIZED|RW_REALIZING|isMeta;
36 cls->setData(rw);// cls 重新将 rw 作为其 data 赋值
37 }
39 #if FAST_CACHE_META
40 if (isMeta) cls->cache.setBit(FAST_CACHE_META);
41 #endif
43 // Choose an index for this class.
44 // Sets cls->instancesRequireRawIsa if indexes no more indexes are available
45 cls->chooseClassArrayIndex();
47 if (PrintConnecting) {
48 _objc_inform("CLASS: realizing class '%s'%s %p %p #%u %s%s",
49 cls->nameForLogging(), isMeta ? " (meta)" : "",
50 (void*)cls, ro, cls->classArrayIndex(),
51 cls->isSwiftStable() ? "(swift)" : "",
52 cls->isSwiftLegacy() ? "(pre-stable swift)" : "");
53 }
55 // Realize superclass and metaclass, if they aren't already.
56 // This needs to be done after RW_REALIZED is set above, for root classes.
57 // This needs to be done after class index is chosen, for root metaclasses.
58 // This assumes that none of those classes have Swift contents,
59 // or that Swift's initializers have already been called.
60 // fixme that assumption will be wrong if we add support
61 // for ObjC subclasses of Swift classes.// cls 的所有的父类 元类 全部实现、确定 --> 继承链确定下来
62 supercls = realizeClassWithoutSwift(remapClass(cls->superclass), nil);// 父类 递归
63 metacls = realizeClassWithoutSwift(remapClass(cls->ISA()), nil);// 元类 递归
65 #if SUPPORT_NONPOINTER_ISA // isa 的设置
66 if (isMeta) {
67 // Metaclasses do not need any features from non pointer ISA
68 // This allows for a faspath for classes in objc_retain/objc_release.
69 cls->setInstancesRequireRawIsa();
70 } else {
71 // Disable non-pointer isa for some classes and/or platforms.
72 // Set instancesRequireRawIsa.
73 bool instancesRequireRawIsa = cls->instancesRequireRawIsa();
74 bool rawIsaIsInherited = false;
75 static bool hackedDispatch = false;
77 if (DisableNonpointerIsa) {
78 // Non-pointer isa disabled by environment or app SDK version
79 instancesRequireRawIsa = true;
80 }
81 else if (!hackedDispatch && 0 == strcmp(ro->name, "OS_object"))
82 {
83 // hack for libdispatch et al - isa also acts as vtable pointer
84 hackedDispatch = true;
85 instancesRequireRawIsa = true;
86 }
87 else if (supercls && supercls->superclass &&
88 supercls->instancesRequireRawIsa())
89 {
90 // This is also propagated by addSubclass()
91 // but nonpointer isa setup needs it earlier.
92 // Special case: instancesRequireRawIsa does not propagate
93 // from root class to root metaclass
94 instancesRequireRawIsa = true;
95 rawIsaIsInherited = true;
96 }
98 if (instancesRequireRawIsa) {
99 cls->setInstancesRequireRawIsaRecursively(rawIsaIsInherited);
100 }
101 }
102 // SUPPORT_NONPOINTER_ISA
103 #endif
104 // 更新父类和元类
105 // Update superclass and metaclass in case of remapping
106 cls->superclass = supercls;
107 cls->initClassIsa(metacls);
109 // Reconcile instance variable offsets / layout.
110 // This may reallocate class_ro_t, updating our ro variable.
111 if (supercls && !isMeta) reconcileInstanceVariables(cls, supercls, ro);
113 // Set fastInstanceSize if it wasn't set already.
114 cls->setInstanceSize(ro->instanceSize);
116 // Copy some flags from ro to rw
117 if (ro->flags & RO_HAS_CXX_STRUCTORS) {
118 cls->setHasCxxDtor();
119 if (! (ro->flags & RO_HAS_CXX_DTOR_ONLY)) {
120 cls->setHasCxxCtor();
121 }
122 }
124 // Propagate the associated objects forbidden flag from ro or from
125 // the superclass.
126 if ((ro->flags & RO_FORBIDS_ASSOCIATED_OBJECTS) ||
127 (supercls && supercls->forbidsAssociatedObjects()))
128 {
129 rw->flags |= RW_FORBIDS_ASSOCIATED_OBJECTS;
130 }
132 // Connect this class to its superclass's subclass lists
133 if (supercls) {
134 addSubclass(supercls, cls);
135 } else {
136 addRootClass(cls);
137 }
139 // Attach categories
140 methodizeClass(cls, previously);
142 return cls;
143 }
我们仍然是通过自己的类
MyPerson
进行分析(系统类毕竟看不见其具体操作,自己写的类更易分析)。
1、源码分析
如下图 2500行
(上面源码23行)
读取
cls
对应的地址中的
data
信息,并对其进行格式强转,
ro
中具体数据信息见下图:
源码
23行起,所做操作:
取 macho 格式中的 data 数据:
ro
(原始,只读的
干净内存
)
进行
class_ro_t
格式强转
--> 开辟
rw(可读写的 脏内存)
空间 --> rw 赋值为 ro --> cls 重新赋值 rw 为 data
.
问题
:1、为何要在开辟出一份
rw
呢? --> 因为 iOS 的运行时,内存会被不断插入添加删除操作,
防止对我们的原始数据的修改,所以 copy 一份干净内存 ro 到 rw 里面
;
2、为何有了一个 rw了 还要有一个
rwe
呢?-->
因为并非每个类都会进行动态的操作,为避免内存的大量操作,我们
只对进行了动态操作的类进行 rwe
。rw 是从 ro 读取拷贝的。
关于
干净/脏内存
可以看下 WWDC 的视频介绍:
Advancements in the Objective-C runtime
。
get_ro
代码:
1)36行
setData(rw)
:
2)我们继续向下执行
(上面源码的62行)
:
realizeClassWithoutSwift()
方法递归:cls 的所有 父类 元类 全部实现确定下来 --> 继承链的确定(这里是 MyPerson 的继承链)
3)继续读代码(上面源码65行起),isa 的设置,继续执行,cls 如下(地址:MyPerson 的元类信息):
此时 bits 仍是无值的,继续执行。
4)
setInstanceSize()
执行后 bits 开始有值了,然后继续对 Cxx 信息处理,见下图:
读取一下地址中的数据,各信息都是存在的(见下图)。而我们的内存 bits 目前是 0x00000034。
放开内部断点直接运行到 main 函数中:
可看到 bits 的内存地址是 0x000280340000003 除了 ‘34’ 其他的值是何时赋的呢?后面再进行探究。
5)
我们继续源码的流程,在 Cxx 的处理之后走到
methodizeClass(
cls, previously
)
,cls 的方法的处理。
2、
methodizeClass()
方法的处理
1 源码2
2 /***********************************************************************
3 * methodizeClass
4 * Fixes up cls's method list, protocol list, and property list.
5 * Attaches any outstanding categories.
6 * Locking: runtimeLock must be held by the caller
7 **********************************************************************/
8 static void methodizeClass(Class cls, Class previously)
10 runtimeLock.assertLocked();
12 bool isMeta = cls->isMetaClass();
13 auto rw = cls->data();
14 auto ro = rw->ro();
15 auto rwe = rw->ext();
17 // Methodizing for the first time
18 if (PrintConnecting) {
19 _objc_inform("CLASS: methodizing class '%s' %s",
20 cls->nameForLogging(), isMeta ? "(meta)" : "");
21 }
23 // Install methods and properties that the class implements itself.
24 method_list_t *list = ro->baseMethods();
25 if (list) {// 方法 list
26 prepareMethodLists(cls, &list, 1, YES, isBundleClass(cls));
27 if (rwe) rwe->methods.attachLists(&list, 1);
28 }
30 property_list_t *proplist = ro->baseProperties;
31 if (rwe && proplist) {// 属性 list
32 rwe->properties.attachLists(&proplist, 1);
33 }
35 protocol_list_t *protolist = ro->baseProtocols;
36 if (rwe && protolist) {// 协议 list
37 rwe->protocols.attachLists(&protolist, 1);
38 }
40 // Root classes get bonus method implementations if they don't have
41 // them already. These apply before category replacements.
42 if (cls->isRootMetaclass()) {
43 // root metaclass
44 addMethod(cls, @selector(initialize), (IMP)&objc_noop_imp, "", NO);
45 }
47 // Attach categories. 附着关联分类
48 if (previously) {
49 if (isMeta) {
50 objc::unattachedCategories.attachToClass(cls, previously,
51 ATTACH_METACLASS);
52 } else {
53 // When a class relocates, categories with class methods
54 // may be registered on the class itself rather than on
55 // the metaclass. Tell attachToClass to look for those.
56 objc::unattachedCategories.attachToClass(cls, previously,
57 ATTACH_CLASS_AND_METACLASS);
58 }
59 }
60 objc::unattachedCategories.attachToClass(cls, cls,
61 isMeta ? ATTACH_METACLASS : ATTACH_CLASS);
63 #if DEBUG
64 // Debug: sanity-check all SELs; log method list contents
65 for (const auto& meth : rw->methods()) {
66 if (PrintConnecting) {
67 _objc_inform("METHOD %c[%s %s]", isMeta ? '+' : '-',
68 cls->nameForLogging(), sel_getName(meth.name));
69 }
70 ASSERT(sel_registerName(sel_getName(meth.name)) == meth.name);
71 }
72 #endif
方法列表的准备,prepareMethodLists(); 。
1 static void
2 prepareMethodLists(Class cls, method_list_t **addedLists, int addedCount,
3 bool baseMethods, bool methodsFromBundle)
5 runtimeLock.assertLocked();
7 if (addedCount == 0) return;
9 // There exist RR/AWZ/Core special cases for some class's base methods.
10 // But this code should never need to scan base methods for RR/AWZ/Core:
11 // default RR/AWZ/Core cannot be set before setInitialized().
12 // Therefore we need not handle any special cases here.
13 if (baseMethods) {
14 ASSERT(cls->hasCustomAWZ() && cls->hasCustomRR() && cls->hasCustomCore());
15 }
17 // Add method lists to array.
18 // Reallocate un-fixed method lists.
19 // The new methods are PREPENDED to the method list array.
21 for (int i = 0; i < addedCount; i++) {
22 method_list_t *mlist = addedLists[i];
23 ASSERT(mlist);
25 // Fixup selectors if necessary
26 if (!mlist->isFixedUp()) {
27 // 方法排序
28 fixupMethodList(mlist, methodsFromBundle, true/*sort*/);
29 }
30 }
32 // If the class is initialized, then scan for method implementations
33 // tracked by the class's flags. If it's not initialized yet,
34 // then objc_class::setInitialized() will take care of it.
35 if (cls->isInitialized()) {
36 objc::AWZScanner::scanAddedMethodLists(cls, addedLists, addedCount);
37 objc::RRScanner::scanAddedMethodLists(cls, addedLists, addedCount);
38 objc::CoreScanner::scanAddedMethodLists(cls, addedLists, addedCount);
39 }
方法排序 fixupMethodList() :
1 static void
2 fixupMethodList(method_list_t *mlist, bool bundleCopy, bool sort)
4 runtimeLock.assertLocked();
5 ASSERT(!mlist->isFixedUp());
7 // fixme lock less in attachMethodLists ?
8 // dyld3 may have already uniqued, but not sorted, the list
9 if (!mlist->isUniqued()) {
10 mutex_locker_t lock(selLock);
12 // Unique selectors in list.
13 for (auto& meth : *mlist) {
14 const char *name = sel_cname(meth.name);
15 meth.name = sel_registerNameNoLock(name, bundleCopy);
16 }
17 }
19 // Sort by selector address.
20 // 根据 方法的地址 进行排序
21 /**
22 若方法重名则根据 sel 排序:
23 1、sel 混乱则进行 fixedUp 2、sel 没有混乱则根据 imp 排序
24 */
26 if (sort) {
27 method_t::SortBySELAddress sorter;
28 /**
29 struct method_t {
30 SEL name;
31 const char *types;
32 MethodListIMP imp;
34 struct SortBySELAddress :
35 public std::binary_function<const method_t&,
36 const method_t&, bool>
37 {
38 bool operator() (const method_t& lhs,
39 const method_t& rhs)
40 { return lhs.name < rhs.name; }// 根据名字进行排序
41 };
42 };
43 */
44 std::stable_sort(mlist->begin(), mlist->end(), sorter);
45 }
47 // Mark method list as uniqued and sorted
48 mlist->setFixedUp();
方法排序前 ro->baseMethods() 的list:
方法排序后 fixupMethodList() 的 list:
类加载流程的执行总结:
以上我们探究的是本类的加载,我们给本类添加分类后是如何加载的呢?
OC 底层探索 15 继续探究。