OC底层探索17 - 类的加载(上)
2021-06-24 本文已影响0人
Henry________
在OC底层探索16 -应用程序加载中提到了dyld与objc的关系,主要是通过两个函数:map_images
、load_images
来完成类的初始化。
一、 类的加载
1、从_objc_init入手
通过dyld
调起libobjc库
的初始化方法_objc_init
,至此进入runtime的初始化流程
。
void _objc_init(void)
{
//objc-环境变量初始化
environ_init();
//线程key的绑定
tls_init();
//C++函数提前执行
static_init();
//runtime的初始化
runtime_init();
//crash奔溃的处理
exception_init();
#if __OBJC2__
//缓存条件初始化
cache_t::init();
#endif
//特殊情况的回调处理
_imp_implementationWithBlock_init();
//map_images load_images注册到dyld中等待执行
_dyld_objc_notify_register(&map_images, load_images, unmap_image);
}
- 最重要的方法
_dyld_objc_notify_register
中传了2个函数map_images
、load_images
,当然这两个函数都是在dyld的主程序初始化时
完成了调用。 - map_images:完成了
所有类的实现
,也就是本文的重点; - load_images:调用所有类中的
+load
方法
2、map_images -> _read_images
注:此部分几个函数代码都很长,所以分几部分进行分析。
void map_images(unsigned count, const char * const paths[],
const struct mach_header * const mhdrs[])
{
return map_images_nolock(count, paths, mhdrs);
}
//代码很长,只放出核心代码
void map_images_nolock(unsigned mhCount, const char * const mhPaths[],
const struct mach_header * const mhdrs[])
{
if (hCount > 0) {
_read_images(hList, hCount, totalClasses, unoptimizedTotalClasses);
}
}
void _read_images(header_info **hList, uint32_t hCount, int totalClasses, int unoptimizedTotalClasses)
{...}
2.1 readClass 类的标记
//代码很长,只放出核心代码
void _read_images(header_info **hList, uint32_t hCount, ...)
{
//获取mach-o中所有的类
classref_t const *classlist = _getObjc2ClassList(hi, &count);
for (i = 0; i < count; i++) {
Class cls = (Class)classlist[i];
Class newCls = readClass(cls, headerIsBundle, headerIsPreoptimized);
}
// 标记所有类完成
ts.log("IMAGE TIMES: discover classes");
...
}
Class readClass(Class cls, bool headerIsBundle, bool headerIsPreoptimized)
{
//从mach-o里获取类名字符串
const char *mangledName = cls->nonlazyMangledName();
if (mangledName) {
//将类名和类绑定存入HashMap中
addNamedClass(cls, mangledName, replacing);
}
//类加载到内存中了
addClassTableEntry(cls);
return cls;
}
2.1.1 addNamedClass
//将类名和类绑定后存入HashMap中
static void addNamedClass(Class cls, const char *name, Class replacing = nil)
{
//插入到类与类名的映射表中
NXMapInsert(gdb_objc_realized_classes, name, cls);
}
2.1.2 addClassTableEntry
//将类的信息加载到内存表中了
static void addClassTableEntry(Class cls, bool addMeta = true)
{
auto &set = objc::allocatedClasses.get();
//把类放入所有类的表中
if (!isKnownClass(cls))
set.insert(cls);
//单次递归把元类也放入所有类的表中
if (addMeta)
addClassTableEntry(cls->ISA(), false);
}
- 首先通过
_getObjc2ClassList
这个方法从mach-o中获取到所有类, - 然后通过
addNamedClass
,addClassTableEntry
两个方法之后将类的信息保存到了内存(表)中,以供后续使用。
2.2 非懒加载类data的加载
//代码很长,只放出核心代码
void _read_images(header_info **hList, uint32_t hCount, ...)
{
//获取所有非懒加载类
classref_t const *classlist = hi->nlclslist(&count);
for (i = 0; i < count; i++) {
//类的重定向
Class cls = remapClass(classlist[i]);
if (!cls) continue;
realizeClassWithoutSwift(cls, nil);
}
ts.log("IMAGE TIMES: realize non-lazy classes");
}
2.2.1 非懒加载类
、懒加载类
-
在oc中
非懒加载类
、懒加载类
主要区别在于类是在哪个阶段进行实现。 -
懒加载类
:没有实现+load
方法;在第一次消息慢速查找时触发加载。
IMP lookUpImpOrForward(id inst, SEL sel, Class cls, int behavior)
{
checkIsKnownClass(cls);
//该方法中完成类数据的加载和初始化
cls = realizeAndInitializeIfNeeded_locked(inst, cls, behavior & LOOKUP_INITIALIZE);
...
}
static Class
realizeAndInitializeIfNeeded_locked(id inst, Class cls, bool initialize)
{
//类若没有实现,则完成类的加载
if (slowpath(!cls->isRealized())) {
cls = realizeClassMaybeSwiftAndLeaveLocked(cls, runtimeLock);
}
//类没有初始化,则完成类的初始化
if (slowpath(initialize && !cls->isInitialized())) {
cls = initializeAndLeaveLocked(cls, inst, runtimeLock);
}
return cls;
}
-
非懒加载类
:实现了+load
方法;在程序启动时完成实现.
2.2.2 realizeClassWithoutSwift类data的加载
static Class realizeClassWithoutSwift(Class cls, Class previously)
{
// 从mach-o里获取类的data
auto ro = (const class_ro_t *)cls->data();
auto isMeta = ro->flags & RO_META;
{
// Normal class. Allocate writeable class data.
//类data的设置
rw = objc::zalloc<class_rw_t>();
rw->set_ro(ro);
rw->flags = RW_REALIZED|RW_REALIZING|isMeta;
cls->setData(rw);
}
#if FAST_CACHE_META
//元类进行标记
if (isMeta) cls->cache.setBit(FAST_CACHE_META);
#endif
// 递归创建父类、元类
supercls = realizeClassWithoutSwift(remapClass(cls->getSuperclass()), nil);
metacls = realizeClassWithoutSwift(remapClass(cls->ISA()), nil);
// Update superclass and metaclass in case of remapping
// 建立父类、元类的关系
cls->setSuperclass(supercls);
cls->initClassIsa(metacls);
// Set fastInstanceSize if it wasn't set already.
// 类的子类在创建(alloc)中直接使用的大小在此处设置
cls->setInstanceSize(ro->instanceSize);
// Copy some flags from ro to rw
//完成类的c++方法设置
if (ro->flags & RO_HAS_CXX_STRUCTORS) {
cls->setHasCxxDtor();
if (! (ro->flags & RO_HAS_CXX_DTOR_ONLY)) {
cls->setHasCxxCtor();
}
}
// Connect this class to its superclass's subclass lists
// 建立类双向链表关系
if (supercls) {
addSubclass(supercls, cls);
} else {
addRootClass(cls);
}
// 类中方法的加载
methodizeClass(cls, previously);
return cls;
}
- 从
mach-0
中读出ro
后,完成类的rw
或者rwe
的设置 - 完成了
父类、元类的实现
,并且在此建立关系
。
2.3 类中方法的加载
static void methodizeClass(Class cls, Class previously)
{
bool isMeta = cls->isMetaClass();
auto rw = cls->data();
auto ro = rw->ro();
auto rwe = rw->ext();
// Install methods and properties that the class implements itself.
//获取类在mach-o的方法data
method_list_t *list = ro->baseMethods();
if (list) {
//mach-o的方法data存在,则对方法SEL地址进行从小到大排序
prepareMethodLists(cls, &list, 1, YES, isBundleClass(cls), nullptr);
// 脏内存若存在直接完成方法的注入
if (rwe) rwe->methods.attachLists(&list, 1);
}
property_list_t *proplist = ro->baseProperties;
if (rwe && proplist) {
//完成属性的注入
rwe->properties.attachLists(&proplist, 1);
}
protocol_list_t *protolist = ro->baseProtocols;
if (rwe && protolist) {
//完成协议的注入
rwe->protocols.attachLists(&protolist, 1);
}
//有分类的情况
objc::unattachedCategories.attachToClass(cls, cls,
isMeta ? ATTACH_METACLASS : ATTACH_CLASS);
}
- 类中的方法在
mach-o读取
后就已经完成了注入,通过prepareMethodLists
这个方法完成了排序,方便后续lookupImpforward中
的二分查找使用
- 属性、协议是读取后通过
attachLists
完成注入。
2.3.1 attachLists中addedCount=1的情况
void attachLists(List* const * addedLists, uint32_t addedCount) {
...
if (hasArray()) { // many lists -> many lists }
else if (!list && addedCount == 1) {
// 0 lists -> 1 list
list = addedLists[0];
}
else { // 1 list -> many lists }
}
- 将需要添加的
新数组(addedLists)首地址赋值给目标数组(list)首地址
,完成赋值。
总结
- 类的加载(上)中,对类的
名称、data、方法、属性、协议
的注入完成了分析。期间还对非懒加载类
、懒加载类
做了简单的介绍。发现一个宗旨就是能晚一点加载就晚一点加载,可以看到苹果开发人员对性能优化做出的努力。 - 由于篇幅问题,类的分类在下文中完成分析。OC底层探索18 - 类的加载(下)