objc-init分析
分别调用了很多方法分别是:
environ_init(): 读取影响运⾏时的环境变量。如果需要,还可以打印环境变量帮助。tls_init():关于线程key的绑定 - ⽐如每线程数据的析构函数static_init():运⾏C ++静态构造函数。在dyld调⽤我们的静态构造函数之前,libc会调⽤_objc_init(),因此我们必须⾃⼰做lock_init(): 没有重写,采⽤C++的特性exception_init()初始化libobjc的异常处理系统cache_init(): 缓存条件初始化runtime_init(): runtime运⾏时环境初始化,⾥⾯主要是:unattachedCategories,allocatedClasses 后⾯会分析_imp_implementationWithBlock_init:启动回调机制。通常这不会做什么,因为所有的初始化都 是惰性的,但是对于某些进程,我们会迫不及待地加载trampolines dylib。 了解即可。- 重点
_dyld_objc_notify_register(&map_images, load_images, unmap_image); 分析
map_images()dyld将image镜像文件加载到内存中会调用该函数。load_images()dyld初始化所有的image镜像文件文件会调用。unmap_image:将image镜像文件移除时会调用。
load_images方法其实就是调用load方法,map_image方法,&map_images是指针传递,指向是同一块实现的地址,如果有什么变化就可以第一时间知道。在dyld中sNotifyObjCMapped调用的地方是在notifyBatchPartial方法中,而notifyBatchPartial方法是在registerObjCNotifiers中调用,在objc初始化注册通知时就调用了,所以是先调用map_images后调用load_images这么个函数执行顺序。
- &map_images 地址值作为参数,即指针传递,数据同步进行变化。map_images 函数是一个不断递归的函数
map_images 函数分析
/***********************************************************************
* map_images
* Process the given images which are being mapped in by dyld.
* Calls ABI-agnostic code after taking ABI-specific locks.
*
* Locking: write-locks runtimeLock
* map_images
*处理由dyld映射的给定图像。映射镜像文件
*在获取abi特定的锁后调用abi不可知代码。
*
*锁:写锁runtimeLock
**********************************************************************/
void
map_images(unsigned count, const char * const paths[],
const struct mach_header * const mhdrs[])
{
mutex_locker_t lock(runtimeLock);
return map_images_nolock(count, paths, mhdrs);
}
- map_images_nolock 由于代码量过多非重点部分省略 ...
/***********************************************************************
* map_images_nolock
* 处理dyld映射到的给定镜像
* 所有的类注册和修正都被执行(或延迟等待)
* 发现丢失的超类等,并调用+load方法
*
* Info[]是自下而上的顺序,即libobjc将在早些时候
* 数组比任何链接到libobjc的库
*
* 锁定:loadMethodLock(旧的)或runtimeLock(新的)由map_images获取
**********************************************************************/
#if __OBJC2__
#include "objc-file.h"
#else
#include "objc-file-old.h"
#endif
void
map_images_nolock(unsigned mhCount, const char * const mhPaths[],
const struct mach_header * const mhdrs[])
{
static bool firstTime = YES;
header_info *hList[mhCount];
uint32_t hCount;
size_t selrefCount = 0;
// 如果需要,执行首次初始化
// 该函数在普通库初始化器之前调用
// 修复延迟初始化直到找到一个使用objc的镜像?
if (firstTime) { ... }
if (PrintImages) { ... }
// 找到所有带有Objective-C元数据的镜像
hCount = 0;
// Count classes. Size various table based on the total.
int totalClasses = 0;
int unoptimizedTotalClasses = 0;
{
uint32_t i = mhCount;
while (i--) { ... }
}
// 执行必须延迟到的一次性运行时初始化
// 可执行文件本身被找到。这需要提前完成
// 进一步的初始化
// 可执行文件可能不在这个infoList中,如果
// 可执行程序不包含Objective-C代码,而是Objective-C
// 稍后动态加载
if (firstTime) { ... }
if (hCount > 0) {
_read_images(hList, hCount, totalClasses, unoptimizedTotalClasses);
}
firstTime = NO;
// 在一切设置完成后调用镜像加载函数
for (auto func : loadImageFuncs) {
for (uint32_t i = 0; i < mhCount; i++) {
func(mhdrs[i]);
}
}
read_images函数分析 主要完成的是以下的几个功能
/***********************************************************************
* _read_images
* 对链接中的头执行初始处理
* 以headerList开头的列表
*
* 称为:map_images_nolock
*
* 锁定:由map_images获取的runtimeLock
**********************************************************************/
void _read_images(header_info **hList, uint32_t hCount, int totalClasses, int unoptimizedTotalClasses)
{
header_info *hi;
uint32_t hIndex;
size_t count;
size_t i;
Class *resolvedFutureClasses = nil;
size_t resolvedFutureClassCount = 0;
static bool doneOnce;
bool launchTime = NO;
TimeLogger ts(PrintImageTimes);
runtimeLock.assertLocked();
#define EACH_HEADER \
hIndex = 0; \
hIndex < hCount && (hi = hList[hIndex]); \
hIndex++
// 1、条件控制进行一次的加载 找到一张全部的表
if (!doneOnce) { ... }
// 2、修复 @selector 引用
// sel 名字 + 地址
// 一个是machO中的地址(有相对偏移地址),一个是dyld加载的地址(需要以此为基准)需要进行修复(重定向)
static size_t UnfixedSelectors;
{ ... }
ts.log("IMAGE TIMES: fix up selector references");
// 3、发现类,修复未解决的未来类
bool hasDyldRoots = dyld_shared_cache_some_image_overridden();
for (EACH_HEADER) { ... }
ts.log("IMAGE TIMES: discover classes");
// 4、修复重新映射的类
// 类列表和非惰性类列表保持未重映射
// 类引用和超级引用被重新映射以进行消息分派
if (!noClassesRemapped()) { ... }
ts.log("IMAGE TIMES: remap classes");
#if SUPPORT_FIXUP
// 5、修复旧的objc_msgSend_fixup调用站点
for (EACH_HEADER) { ... }
ts.log("IMAGE TIMES: fix up objc_msgSend_fixup");
#endif
// 6、发现协议,修复协议参考
for (EACH_HEADER) { ... }
ts.log("IMAGE TIMES: discover protocols");
// 7、修复 @protocol 引用
// 预优化图像可能有此权限
// 已经回答了,但我们不确定
for (EACH_HEADER) { ... }
ts.log("IMAGE TIMES: fix up @protocol references");
// 8、发现类别,只有在初始类别之后才这样做
// 附件已完成,对于在启动时出现的类别
// 发现被延迟到第一次load_images调用之后
// 对_dyld_objc_notify_register的调用完成
if (didInitialAttachCategories) { ... }
ts.log("IMAGE TIMES: discover categories");
// 9、类别发现必须晚,以避免潜在的竞赛
// 当其他线程之前调用新的类别代码时
// 这个线程完成它的修复
// 10、 +加载由prepare_load_methods()处理
// 实现非惰性类(用于 实现了 +load 方法的静态实例)
for (EACH_HEADER) { ... }
ts.log("IMAGE TIMES: realize non-lazy classes");
// 实现新解析的未来类,以防CF操纵它们
if (resolvedFutureClasses) { ... }
ts.log("IMAGE TIMES: realize future classes");
if (DebugNonFragileIvars) {
realizeAllClasses();
}
// 打印过程中统计
if (PrintPreopt) { ... }
#undef EACH_HEADER
}
- 1、条件控制进行一次的加载
- 2、修复预编译阶段‘@selector’的混乱问题
- 3、错误混乱的类处理
- 4、修复重映射一些没有被镜像文件加载进来的类
- 5、修复一些消息
- 6、当我们类里面有协议的时候:readProtocol
- 7、修复没有被加载的协议
- 8、分类处理
- 9、类的加载处理
- 10、没有被处理的类优化那些被侵犯的类
1.条件控制进行一次的加载 doneOnce
if (!doneOnce) {
doneOnce = YES;
launchTime = YES;
...
...
// namedClasses
// Preoptimized classes don't go in this table.
// 4/3 is NXMapTable's load factor
// objc::unattachedCategories.init(32);
// objc::allocatedClasses.init(); //(生成一张被开辟alloc的表)
int namedClassesSize =
(isPreoptimized() ? unoptimizedTotalClasses : totalClasses) * 4 / 3;
//创建了一个表(总表关于类名的表,不管是否有被实现)
gdb_objc_realized_classes =
NXCreateMapTable(NXStrValueMapPrototype, namedClassesSize);
ts.log("IMAGE TIMES: first time tasks");
}
- 加载一次下次就不会再次进入判断,第一次进来主要创建表
gbd_objc_realized_classes,这是一张关于所有类的不管是否有没有被实现都存放进入。是一张总表。 - 主要作用是生成一张关于全部类名的总表,不管这个类是否有被实现
alloc init allocatedClass.init(),生成一张已经被开辟内存的表。
2.修复预编译阶段‘@selector’的混乱问题
// Fix up @selector references
// sel 名字 + 地址
static size_t UnfixedSelectors;
{
mutex_locker_t lock(selLock);
for (EACH_HEADER) {
if (hi->hasPreoptimizedSelectors()) continue;
bool isBundle = hi->isBundle();
SEL *sels = _getObjc2SelectorRefs(hi, &count);
UnfixedSelectors += count;
for (i = 0; i < count; i++) {
const char *name = sel_cname(sels[i]);
SEL sel = sel_registerNameNoLock(name, isBundle);
if (sels[i] != sel) {
sels[i] = sel;
}
}
}
}
- 不同的类中可能存在相同的方法,但是方法名相当地址是不同的。
- 如:两个方法名相同,但是地址不同,需要进行局部处理。类的地址重定向。
sels[i]是_getObjc2SelectorRefs是从MachO里面获取的,MachO有相对位移地址和偏移地址,sel是sel_registerNameNoLock从dyld里面获取,dyld是链接整个程序的,所以以dyld的为准。因为方法是存放在类中,每个类中的位置是不一样的,所以方法的地址也就不一样,那么就必须对那些混乱的方法进行修复处理。
3、错误混乱的类处理
// 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;
}
}
}
ts.log("IMAGE TIMES: discover classes");
- 清除没有清理干净的类
- 类的地址读取加载。
- readClass方法的作用:把类名和地址关联起来。
readClass 类的读取 现在开始研究我们自己写的类是如何加载的
/***********************************************************************
* readClass
* Read a class and metaclass as written by a compiler.
* Returns the new class pointer. This could be:
* - cls
* - nil (cls has a missing weak-linked superclass)
* - something else (space for this class was reserved by a future class)
*
* Note that all work performed by this function is preflighted by
* mustReadClasses(). Do not change this function without updating that one.
*
* Locking: runtimeLock acquired by map_images or objc_readClassPair
**********************************************************************/
Class readClass(Class cls, bool headerIsBundle, bool headerIsPreoptimized)
{
//获取类名
const char *mangledName = cls->nonlazyMangledName();
const char *LGPersonName = "LGPerson";
if (strcmp(mangledName, LGPersonName) == 0) {
// 普通写得类 他是如何
printf("%s -KC: 要研究的: - %s\n",__func__,mangledName);
}
if (missingWeakSuperclass(cls)) {
// No superclass (probably weak-linked).
// Disavow any knowledge of this subclass.
if (PrintConnecting) {
_objc_inform("CLASS: IGNORING class '%s' with "
"missing weak-linked superclass",
cls->nameForLogging());
}
addRemappedClass(cls, nil);
cls->setSuperclass(nil);
return nil;
}
cls->fixupBackwardDeployingStableSwift();
Class replacing = nil;
if (mangledName != nullptr) {
if (Class newCls = popFutureNamedClass(mangledName)) {
// This name was previously allocated as a future class.
// Copy objc_class to future class's struct.
// Preserve future's rw data block.
if (newCls->isAnySwift()) {
_objc_fatal("Can't complete future class request for '%s' "
"because the real class is too big.",
cls->nameForLogging());
}
class_rw_t *rw = newCls->data();
const class_ro_t *old_ro = rw->ro();
memcpy(newCls, cls, sizeof(objc_class));
// Manually set address-discriminated ptrauthed fields
// so that newCls gets the correct signatures.
newCls->setSuperclass(cls->getSuperclass());
newCls->initIsa(cls->getIsa());
rw->set_ro((class_ro_t *)newCls->data());
newCls->setData(rw);
freeIfMutable((char *)old_ro->getName());
free((void *)old_ro);
addRemappedClass(cls, newCls);
replacing = cls;
cls = newCls;
}
}
if (headerIsPreoptimized && !replacing) {
// class list built in shared cache
// fixme strict assert doesn't work because of duplicates
// ASSERT(cls == getClass(name));
ASSERT(mangledName == nullptr || getClassExceptSomeSwift(mangledName));
} else {
if (mangledName) { //some Swift generic classes can lazily generate their names
addNamedClass(cls, mangledName, replacing);
} else {
Class meta = cls->ISA();
const class_ro_t *metaRO = meta->bits.safe_ro();
ASSERT(metaRO->getNonMetaclass() && "Metaclass with lazy name must have a pointer to the corresponding nonmetaclass.");
ASSERT(metaRO->getNonMetaclass() == cls && "Metaclass nonmetaclass pointer must equal the original class.");
}
addClassTableEntry(cls);
}
// for future reference: shared cache never contains MH_BUNDLEs
if (headerIsBundle) {
cls->data()->flags |= RO_FROM_BUNDLE;
cls->ISA()->data()->flags |= RO_FROM_BUNDLE;
}
return cls;
}
- 1.在自己写的类创建之前断点。
- 2.写上语句判断打印并断点
const char *LGPersonName = "LGPerson";
if (strcmp(mangledName, LGPersonName) == 0) {
// 普通写得类 他是如何
printf("%s -KC: 要研究的: - %s\n",__func__,mangledName);
}
- 3.打印信息提示
加载类流程
- 1.
readClass-> 这里并没有对类的ro,rw的赋值 nonlazyMangleName 获取类型 - 2.
addNameClass(cls,magleName,replacing)-> 将类型和地址关联起来 - 3.
addClassTableEntry(cls),在这里加载类以及元类inset(cls)和addMeta - 这里主要完成的是将类加入到表中,将这个类名的类以及元类插入到另一张哈希表中。这张表中的类都是初始化过的类
realizeClassWithoutSwift(cls, nil); 的分析流程 重点分析
- 主要作用是 处理了ro rw 父类 元类isa 的相关处理
/***********************************************************************
* realizeClassWithoutSwift
* 对类cls执行首次初始化
* 包括分配它的读写数据
* 没有执行任何swift端初始化
* 返回类的实际类结构
* 锁定:runtimeLock必须被调用者写锁定
- 主要作用是 处理了ro rw 父类 元类isa 的相关处理
**********************************************************************/
static Class realizeClassWithoutSwift(Class cls, Class previously)
{
runtimeLock.assertLocked();
class_rw_t *rw;
Class supercls;
Class metacls;
if (!cls) return nil;
if (cls->isRealized()) {
validateAlreadyRealizedClass(cls);
return cls;
}
ASSERT(cls == remapClass(cls));
// 修复验证类不在共享缓存的未打开部分?
// 此处开辟了ro的内存空间地址,其内部并没有加载内容数据
// 举个例子:你买了个房子,现在只是写上了你的名字,但是房子里还是空着的,可能你去家具城定好了家具,但,此刻还没有布置到房子里去
auto ro = (const class_ro_t *)cls->data();
auto isMeta = ro->flags & RO_META;
if (ro->flags & RO_FUTURE) {
// 这是未来的课程。已经分配了Rw数据
rw = cls->data();
ro = cls->data()->ro();
ASSERT(!isMeta);
cls->changeInfo(RW_REALIZED|RW_REALIZING, RW_FUTURE);
} else {
// 正常的类。分配可写的类数据
// 将干净的内存 ro 复制一份到 rw
rw = objc::zalloc<class_rw_t>();
rw->set_ro(ro);
rw->flags = RW_REALIZED|RW_REALIZING|isMeta;
cls->setData(rw);
}
cls->cache.initializeToEmptyOrPreoptimizedInDisguise();
#if FAST_CACHE_META
if (isMeta) cls->cache.setBit(FAST_CACHE_META);
#endif
// 为这个类选择一个索引
// 如果索引没有更多的索引可用,设置cls->instancesRequireRawIsa
cls->chooseClassArrayIndex();
if (PrintConnecting) {
_objc_inform("CLASS: realizing class '%s'%s %p %p #%u %s%s",
cls->nameForLogging(), isMeta ? " (meta)" : "",
(void*)cls, ro, cls->classArrayIndex(),
cls->isSwiftStable() ? "(swift)" : "",
cls->isSwiftLegacy() ? "(pre-stable swift)" : "");
}
// 实现超类和元类,如果它们还没有实现的话
// 对于根类,需要在上面设置rw_realize之后完成
// 这需要在为根元类选择类索引之后进行
// 这假设这些类都没有Swift内容
// 或者Swift的初始化器已经被调用
// 修正一下,如果我们添加支持,这个假设将是错误的
// 对于Swift类的ObjC子类
supercls = realizeClassWithoutSwift(remapClass(cls->getSuperclass()), nil);
metacls = realizeClassWithoutSwift(remapClass(cls->ISA()), nil);
#if SUPPORT_NONPOINTER_ISA
if (isMeta) {
// 元类不需要来自非指针ISA的任何特性
// 这允许为objc_retain/objc_release中的类提供一个路径
cls->setInstancesRequireRawIsa();
} else {
// 对于某些类和/或平台禁用非指针
// 设置 instancesRequireRawIsa.
bool instancesRequireRawIsa = cls->instancesRequireRawIsa();
bool rawIsaIsInherited = false;
static bool hackedDispatch = false;
if (DisableNonpointerIsa) {
// 非指针是禁用的环境或应用程序SDK版本
instancesRequireRawIsa = true;
}
else if (!hackedDispatch && 0 == strcmp(ro->getName(), "OS_object"))
{
// libdispatch等人还充当虚函数表指针
hackedDispatch = true;
instancesRequireRawIsa = true;
}
else if (supercls && supercls->getSuperclass() &&
supercls->instancesRequireRawIsa())
{
// 这也是由addSubclass()传播的
// 但是非指针需要更早的设置
// 特殊情况:instancesRequireRawIsa不传播
// 从根类到根元类
instancesRequireRawIsa = true;
rawIsaIsInherited = true;
}
if (instancesRequireRawIsa) {
cls->setInstancesRequireRawIsaRecursively(rawIsaIsInherited);
}
}
// SUPPORT_NONPOINTER_ISA
#endif
// 更新超类和元类,以防重新映射
cls->setSuperclass(supercls);
cls->initClassIsa(metacls);
// 协调实例变量偏移量/布局
// 这可能会重新分配class_ro_t,更新ro变量
if (supercls && !isMeta) reconcileInstanceVariables(cls, supercls, ro);
// 如果还没有设置,请设置fastInstanceSize
cls->setInstanceSize(ro->instanceSize);
// Copy some flags from ro to rw
if (ro->flags & RO_HAS_CXX_STRUCTORS) {
cls->setHasCxxDtor();
if (! (ro->flags & RO_HAS_CXX_DTOR_ONLY)) {
cls->setHasCxxCtor();
}
}
// 从ro或from传播关联对象禁止标志
// the superclass.
if ((ro->flags & RO_FORBIDS_ASSOCIATED_OBJECTS) ||
(supercls && supercls->forbidsAssociatedObjects()))
{
rw->flags |= RW_FORBIDS_ASSOCIATED_OBJECTS;
}
// 将这个类连接到它的父类的子类列表
if (supercls) {
addSubclass(supercls, cls);
} else {
addRootClass(cls);
}
// 附加类别
methodizeClass(cls, previously);
return cls;
}
- ro: 只读沙盒路径下的内存
- rw: 脏内存ro的复制用于修改的内存
- rwe: extation 拓展类
methodizeClass(cls, previously)
- methodelizeClass() 相关方法的排序
/***********************************************************************
* methodizeClass
* 修复cls的方法列表、协议列表和属性列表
* 附加任何突出类别
* 锁定:runtimeLock必须由调用者持有
**********************************************************************/
static void methodizeClass(Class cls, Class previously)
{
runtimeLock.assertLocked();
bool isMeta = cls->isMetaClass();
auto rw = cls->data();
auto ro = rw->ro();
auto rwe = rw->ext();
const char *mangledName = cls->nonlazyMangledName();
// 第一次系统化
if (PrintConnecting) {
_objc_inform("CLASS: methodizing class '%s' %s",
cls->nameForLogging(), isMeta ? "(meta)" : "");
}
// 安装类自身实现的方法和属性
method_list_t *list = ro->baseMethods(); //读取到了类的方法列表
if (list) {
prepareMethodLists(cls, &list, 1, YES, isBundleClass(cls), nullptr);
// 并没有对 rwe 赋值, 什么时候赋值的呢?
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);
}
// 如果根类没有额外的方法实现,它们会得到额外的方法实现
// 他们已经,这些适用于类别替换之前
if (cls->isRootMetaclass()) {
// 根元类
addMethod(cls, @selector(initialize), (IMP)&objc_noop_imp, "", NO);
}
// 附加类别
if (previously) {
if (isMeta) {
objc::unattachedCategories.attachToClass(cls, previously,
ATTACH_METACLASS);
} else {
// 当一个类重新定位时,用类方法分类
// 可以注册在课程本身而不是
// 元类。告诉attachToClass寻找这些
objc::unattachedCategories.attachToClass(cls, previously,
ATTACH_CLASS_AND_METACLASS);
}
}
objc::unattachedCategories.attachToClass(cls, cls,
isMeta ? ATTACH_METACLASS : ATTACH_CLASS);
#if DEBUG
// Debug: 检查所有SELs;日志方法列表内容
for (const auto& meth : rw->methods()) {
if (PrintConnecting) {
_objc_inform("METHOD %c[%s %s]", isMeta ? '+' : '-',
cls->nameForLogging(), sel_getName(meth.name()));
}
ASSERT(sel_registerName(sel_getName(meth.name())) == meth.name());
}
#endif
}
- prepareMehodList 获取ro中的baseMethods方法列表。然后将方法名和地址相关联,再进行类排序。 ro中的baseMethods()方法 方法名写入+排序
...
// 向数组中添加方法列表
// 重新分配未固定的方法列表
// 新方法被前置到方法列表数组中
for (int i = 0; i < addedCount; i++) {
method_list_t *mlist = addedLists[i];
ASSERT(mlist);
// 如有必要,修正选择器
if (!mlist->isFixedUp()) {
fixupMethodList(mlist, methodsFromBundle, true/*sort*/);
}
}
...
fixupMethodList
static void
fixupMethodList(method_list_t *mlist, bool bundleCopy, bool sort)
{
runtimeLock.assertLocked();
ASSERT(!mlist->isFixedUp());
// fixme锁定更少的attachMethodLists ?
// Dyld3可能已经对列表进行了惟一化,但没有排序
if (!mlist->isUniqued()) {
mutex_locker_t lock(selLock);
// 列表中唯一的选择器
for (auto& meth : *mlist) {
const char *name = sel_cname(meth.name());
// 拿到SEL 将名字 和 地址 设置 到 meth 中
meth.setName(sel_registerNameNoLock(name, bundleCopy));
}
}
// 按选择器地址排序
// 不要试图对小列表进行排序,因为它们是不可变的
// 不要试图对非标准大小的大列表进行排序,如stable_sort
// 不能正确复制条目
if (sort && !mlist->isSmallList() && mlist->entsize() == method_t::bigSize) {
method_t::SortBySELAddress sorter;
std::stable_sort(&mlist->begin()->big(), &mlist->end()->big(), sorter);
}
// 将方法列表标记为惟一的和已排序的
// 不能标记小列表,因为它们是不可变的
if (!mlist->isSmallList()) {
mlist->setFixedUp();
}
}
懒加载与非懒加载类 :
- 当前类是否实现load方法,load方法会让一个类从懒加载类成为一个非懒加载类,懒加载类的好处是能够按需分配内存,节约内存。
- 懒加载类和非懒加载类的加载顺序是不同的,懒加载类将类信息的加载推迟到了第一次发消息的时候,也就是懒加载是按需加载的。
- 1.懒加载类情况 : 数据加载推迟到第一次消息的时候
- lookUpImpOrForward //慢速查找
- realizeClassMaybeSwiftMaybeRelock
- realizeClassWithoutSwift
- methodizeClass
- 2.非懒加载类情况 : map_images的时候加载了所有类数据
- readClass
- _getObjc2NonlazyClassList
- realizeClassWithoutSwift
- methodizeClass
分类的本质
#import <Foundation/Foundation.h>
#import "LGPerson.h"
#import <objc/runtime.h>
extern void _objc_autoreleasePoolPrint(void);
@interface LGPerson (LG)
@property (nonatomic, copy, nullable) NSString *cate_name;
@property (nonatomic, assign) int cate_age;
- (void)cate_instanceMethod1;
- (void)cate_instanceMethod2;
+ (void)cate_classMethod3;
@end
@implementation LGPerson (LG)
- (void)cate_instanceMethod1 {
NSLog(@"%s",__func__);
}
- (void)cate_instanceMethod2 {
NSLog(@"%s",__func__);
}
+ (void)cate_classMethod3 {
NSLog(@"%s",__func__);
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
LGPerson *p = [LGPerson alloc];
[p say1];
NSLog(@"Hello, World!");
}
return 0;
}
__attribute__((constructor)) void kcFunc(void){
printf("来了 : %s \n",__func__);
}
我们在 main.m 为 LGPerson 类添加一个分类 LGPerson (LG),cd 到 main.m 所在文件目录,然后输入 clang -rewrite-objc main.m -o main.cpp 命令,最后会看到生成了一个 main.cpp 文件。
我们打开 main.cpp 文件,在最后可以看到 LGPerson (LG) 的底层代码。接着我们点进到 _category_t 查看分类的结构。我们可以看到结构体里面有 name,这里 name 的名称就是 LG,cls 这里就是就是 LGPerson 类,而且可以看到 instance_methods 跟 class_methods,这里相对于类的结构多了 class_methods,这是因为分类没有元类,接着就是 protocols(协议) 跟 properties(关联属性)。
这里我们也可以看到对应我们添加的类方法跟对象方法,但是这里没有 set 方法跟 get 方法,也验证了分类添加属性是通过关联对象进行处理的。这里是生成的 cpp 文件我们看到的,那么在源码中分类的结构是否跟这里一致呢?
这里可以看到结构跟我们在 cpp 文件中看到的基本一直,只是多了一个 _classProperties,但是类属性不是一直都有。
ro、rw、rwe
ro:app 在使用类时,是需要在磁盘中 app 的二进制文件中读取类的信息,二进制文件中的类存储了类的元类、父类、flags 和方法缓存,而类的额外信息(name、方法、协议和实例变量等)存储在class_ro_t中。class_ro_t简称ro,read only,将类从磁盘中读取到内存中就是对ro的赋值操作。由于ro是只读的,加载到内存后不会发生改变又称为clean memory(干净内存)。
由
cls->data()强转class_ro_t得到
-
rw:class_rw_t简称rw,read write,用于读写编写程序。drity memory在进程运行时发生更改的内存。类一经使用运行时就会分配一个额外的内存,那么这个内存变成了drity memory。但是在实际应用中,类的使用量只是 10%,这样就在rw中造成了内存浪费,所以苹果就把rw中方法、协议和实例变量等放到了class_rw_ext_t中。 -
rwe:class_rw_ext_t简称rwe,read write ext,用于运行时存储类的方法、协议和实例变量等信息。
rwe的加载 attachCategories
搜索可以看到分别在 load_categories_nolock 跟 attachToClass 这两个地方调用了 attachCategories 函数。
接着我们搜索 attachToClass 发现有三个地方调用了这个函数,但是都是在 methodizeClass 函数里面调用的。我们看到代码逻辑走到 if (previously) {} 的前提是 previously 有值,而 previously 是函数参数,所以我们搜索 methodizeClass 函数。
attachCategories
- rwe 有函数 attachLists 获得
void attachLists(List* const * addedLists, uint32_t addedCount) {
if (addedCount == 0) return;
if (hasArray()) {
// many lists -> many lists
uint32_t oldCount = array()->count;
uint32_t newCount = oldCount + addedCount;
array_t *newArray = (array_t *)malloc(array_t::byteSize(newCount));
newArray->count = newCount;
array()->count = newCount;
for (int i = oldCount - 1; i >= 0; i--)
newArray->lists[i + addedCount] = array()->lists[i];
for (unsigned i = 0; i < addedCount; i++)
newArray->lists[i] = addedLists[i];
free(array());
setArray(newArray);
validate();
}
else if (!list && addedCount == 1) {
// 0 lists -> 1 list
list = addedLists[0];
validate();
}
else {
// 1 list -> many lists
Ptr<List> oldList = list;
uint32_t oldCount = oldList ? 1 : 0;
uint32_t newCount = oldCount + addedCount;
setArray((array_t *)malloc(array_t::byteSize(newCount)));
array()->count = newCount;
if (oldList) array()->lists[addedCount] = oldList;
for (unsigned i = 0; i < addedCount; i++)
array()->lists[i] = addedLists[i];
validate();
}
}