上篇已经说明了 map_images,但没有详细说明,下面我们来继续..
1、_read_images分析
1、条件控制进行一次的加载
2、修复预编译阶段的 @selector的混乱问题
3、错误混乱的类处理
4、修复重映射一些没有被镜像文件加载进来的类
5、修复一些消息
6、当我们类里面有协议的时候:readProtocol
7、修复没有被加载的协议
8、分类处理
9、类的加载处理
10、没有被处理的类 优化那些侵犯的类
void _read_images(header_info **hList, uint32_t hCount, int totalClasses, int unoptimizedTotalClasses)
{
里面的代码太多,就不一一说明了
}
// Discover classes. Fix up unresolved future classes. Mark bundle classes.
bool hasDyldRoots = dyld_shared_cache_some_image_overridden();
for (EACH_HEADER) {
if (! mustReadClasses(hi, hasDyldRoots)) {
// Image is sufficiently optimized that we need not call readClass()
continue;
}
classref_t const *classlist = _getObjc2ClassList(hi, &count);
bool headerIsBundle = hi->isBundle();
bool headerIsPreoptimized = hi->hasPreoptimizedClasses();
//确定类的继承关系
for (i = 0; i < count; i++) {
Class cls = (Class)classlist[i];
Class newCls = readClass(cls, headerIsBundle, headerIsPreoptimized);
if (newCls != cls && newCls) {
// Class was moved but not deleted. Currently this occurs
// only when the new class resolved a future class.
// Non-lazily realize the class below.
resolvedFutureClasses = (Class *)
realloc(resolvedFutureClasses,
(resolvedFutureClassCount+1) * sizeof(Class));
resolvedFutureClasses[resolvedFutureClassCount++] = newCls;
}
}
}
2、readClass 分析
由上可以得到 cls 和 类名 关联起来..
image.png增加条件判断--指定判断类
image.png这个if语句肯定不会走的
if (headerIsPreoptimized && !replacing) {
// class list built in shared cache
// fixme strict assert doesn't work because of duplicates
// ASSERT(cls == getClass(name));
ASSERT(getClassExceptSomeSwift(mangledName));
} else {
那直接来到了这里了..
addNamedClass(cls, mangledName, replacing);
addClassTableEntry(cls);
}
addNamedClass,给了cls名字
image.png3、addNamedClass 方法分析
static void addNamedClass(Class cls, const char *name, Class replacing = nil)
{
runtimeLock.assertLocked();
Class old;
if ((old = getClassExceptSomeSwift(name)) && old != replacing) {
inform_duplicate(name, old, cls);
// getMaybeUnrealizedNonMetaClass uses name lookups.
// Classes not found by name lookup must be in the
// secondary meta->nonmeta table.
addNonMetaClass(cls);
} else {
NXMapInsert(gdb_objc_realized_classes, name, cls);
}
ASSERT(!(cls->data()->flags & RO_META));
// wrong: constructed classes are already realized when they get here
// ASSERT(!cls->isRealized());
}
image.png
走完NXMapInsert方法之后,这个类名就关联起来了~
二、懒加载类与非懒加载类
懒加载类与非懒加载类的区别:当前类是否实现load方法(load方法在mian函数之前)
1、非懒加载类情况
map_images的时候 加载所有类数据:
- _getObjc2NonlazyClassList
- readClass
- realizeClassWithoutSwift
- methodizeClass
们也可以根据_read_images中的注释分析 ,当我们调试的时候没有走到这里,说明我们没有实现load,要是我们实现load的话是不是在这里就可以观察类的加载情况呢?
image.png image.png image.png读取class
的data
数据,并将其强转为ro
,以及rw初始化
和ro拷贝一份到rw中的ro
-
ro
表示readOnly
,即只读
,其在编译时就已经确定了内存,包含类名称、方法、协议和实例变量的信息,由于是只读的,所以属于Clean Memory
,而Clean Memory
是指加载后不会发生更改的内存
-
rw
表示readWrite
,即可读可写
,由于其动态性,可能会往类中添加属性、方法、添加协议,在最新的2020的WWDC
的对内存优化
的说明Advancements in the Objective-C runtime - WWDC 2020 - Videos - Apple Developer中,提到rw
,其实在rw
中只有10%的类真正的更改了它们的方法,所以有了rwe
,即类的额外信息
。对于那些确实需要额外信息的类,可以分配rwe扩展记录中的一个,并将其滑入类中供其使用。其中rw
就属于dirty memory
,而dirty memory
是指在进程运行时会发生更改的内存
,类结构
一经使用
就会变成ditry memory
,因为运行时会向它写入新数据,例如 创建一个新的方法缓存,并从类中指向它
我们通过断点调试,发现他走的是 realizeClassWithoutSwift(cls, nil); (实现类)
image.png image.png递归调用realizeClassWithoutSwift
完善继承链
,并设置当前类、父类、元类的rw
递归调用 realizeClassWithoutSwift
设置父类
、元类
设置父类和元类的isa指向
通过addSubclass
和 addRootClass
设置父子的双向链表指向关系,即父类中可以找到子类,子类中可以找到父类
2、懒加载类情况
数据加载推迟到第⼀次消息的时候
- LookUpImpOrForword
- realizeClassMaybeSwiftMaybeRelock
- realizeClassWithoutSwift
- methodizeClass
我们知道非懒加载怎么加的方法属性。那么懒加载呢?(把load去掉,然后调用一波),我们可以通过bt,或者 看堆栈
image.png总结:
image.png
网友评论