继续上篇探索类加载,之前我们进入了_read_images,我们大概看了下主要做了什么,其中实现oc的类是方法realizeClassWithoutSwift,继续下面的探索。
1.类的加载流程分析
首先我们想要断点进入我们自己的类,进行查看类的加载。但是LGPerson这个类都已经
调用了方法,说明已经加载过类的信息了,还没有进入断点。上面的注释说明这是一个非懒加载实现类,我们知道在dyld流程中。load_images时会便利类的+load()方法,有的话实现这个方法。而实现类的方法必然要实现这个类。所以会进入非懒加载的实现类的流程。
进入了,说明调用类的load的方法会进入
realizeClassWithoutSwift
1.1realizeClassWithoutSwift分析
/***********************************************************************
* realizeClassWithoutSwift
* Performs first-time initialization on class cls,
* including allocating its read-write data.
* Does not perform any Swift-side initialization.
* Returns the real class structure for the class.
* Locking: runtimeLock must be write-locked by the caller
对类cls执行首次初始化,包括分配其读写数据,不执行任何swift端初始化,runtimeLock必须被调用者写锁定
**********************************************************************/
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);//实现了话直接返回cls
return cls;
}
ASSERT(cls == remapClass(cls));
// fixme verify class is not in an un-dlopened part of the shared cache?
//修复验证类不在共享缓存的未打开部分
// const char *
// const char * systemClassName = []
auto ro = (const class_ro_t *)cls->data();
auto isMeta = ro->flags & RO_META;
if (ro->flags & RO_FUTURE) {
// This was a future class. rw data is already allocated.
// 这是未来的类。已经分配了rw数据。
rw = cls->data();
ro = cls->data()->ro();
ASSERT(!isMeta);
cls->changeInfo(RW_REALIZED|RW_REALIZING, RW_FUTURE);
} else {
// Normal class. Allocate writeable class data.
//正常的类。分配可写的类数据。
rw = objc::zalloc<class_rw_t>();
rw->set_ro(ro);
rw->flags = RW_REALIZED|RW_REALIZING|isMeta;
cls->setData(rw);
}
const char *systemClassName = cls ->nonlazyMangledName();
const char *LGPersonName = "LGPerson";
if (strcasecmp(systemClassName, LGPersonName) == 0) {
if (!isMeta) {
printf("自己创建的类的方法:%s-%s",__func__,systemClassName);
}
}
cls->cache.initializeToEmptyOrPreoptimizedInDisguise();
#if FAST_CACHE_META
if (isMeta) cls->cache.setBit(FAST_CACHE_META);
#endif
// Choose an index for this class.
//为这个类选择一个索引。
// Sets cls->instancesRequireRawIsa if indexes no more indexes are available
//如果索引没有更多的索引可用,设置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)" : "");
}
// Realize superclass and metaclass, if they aren't already.实现父类和元类,如果它们还没有。
// This needs to be done after RW_REALIZED is set above, for root classes.这需要在上面设置rw_realize之后完成,对于根类。
// This needs to be done after class index is chosen, for root metaclasses.这需要在根元类的类索引被选择之后完成。
// This assumes that none of those classes have Swift contents,
// or that Swift's initializers have already been called.假设这些类都没有Swift内容,或者Swift的初始化器已经被调用。
// fixme that assumption will be wrong if we add support
// for ObjC subclasses of Swift classes. 自我修复如果我们添加支持,假设将是错误的用于Swift类的ObjC子类。
supercls = realizeClassWithoutSwift(remapClass(cls->getSuperclass()), nil);//实现父类链
metacls = realizeClassWithoutSwift(remapClass(cls->ISA()), nil);//实现元类链关联,也就是isa
#if SUPPORT_NONPOINTER_ISA
if (isMeta) {
// Metaclasses do not need any features from non pointer ISA
// This allows for a faspath for classes in objc_retain/objc_release.
cls->setInstancesRequireRawIsa();
} else {
// Disable non-pointer isa for some classes and/or platforms.
// Set instancesRequireRawIsa.
bool instancesRequireRawIsa = cls->instancesRequireRawIsa();
bool rawIsaIsInherited = false;
static bool hackedDispatch = false;
if (DisableNonpointerIsa) {
// Non-pointer isa disabled by environment or app SDK version//不是纯的isa
instancesRequireRawIsa = true;
}
else if (!hackedDispatch && 0 == strcmp(ro->getName(), "OS_object"))
{
// hack for libdispatch et al - isa also acts as vtable pointer
hackedDispatch = true;
instancesRequireRawIsa = true;
}
else if (supercls && supercls->getSuperclass() &&
supercls->instancesRequireRawIsa())
{
// This is also propagated by addSubclass()
// but nonpointer isa setup needs it earlier.
// Special case: instancesRequireRawIsa does not propagate
// from root class to root metaclass
instancesRequireRawIsa = true;
rawIsaIsInherited = true;
}
if (instancesRequireRawIsa) {
cls->setInstancesRequireRawIsaRecursively(rawIsaIsInherited);
}
}
// SUPPORT_NONPOINTER_ISA
#endif
// Update superclass and metaclass in case of remapping
//更新父类和元类,以防重新映射
cls->setSuperclass(supercls);
cls->initClassIsa(metacls);
// Reconcile instance variable offsets / layout.
// This may reallocate class_ro_t, updating our ro variable.
if (supercls && !isMeta) reconcileInstanceVariables(cls, supercls, ro);
// Set fastInstanceSize if it wasn't set already.
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();
}
}
// Propagate the associated objects forbidden flag from ro or from
// the superclass.
if ((ro->flags & RO_FORBIDS_ASSOCIATED_OBJECTS) ||
(supercls && supercls->forbidsAssociatedObjects()))
{
rw->flags |= RW_FORBIDS_ASSOCIATED_OBJECTS;
}
// Connect this class to its superclass's subclass lists
if (supercls) {
addSubclass(supercls, cls);
} else {
addRootClass(cls);
}
// Attach categories 排序和添加分类
methodizeClass(cls, previously);
return cls;
}
- 我们首先判断这个类
是否加载过了,加载过就直接返回。 - 读取类的数据data编译时
确定的数据,赋值给ro,之后把ro复制一份给rw。ro在之后运行中不在改变,rw数据则可以读写,如果需要新增,动态添加则写在rwe中。 - 初始化类的
缓存 - 设置类的索引,确定自身的父类,和元类也就是isa指向,递归完成
父类链和isa走位。 更新父类和元类,以防重新映射- 给类的方法和属性,协议等地址
排序,添加分类categories。
1.2 methodizeClass分析
/***********************************************************************
* methodizeClass 给类进行排序,主要方法,属性,协议排序
* Fixes up cls's method list, protocol list, and property list.修复方法列表,协议列表,属性列表。
* Attaches any outstanding categories.添加任何未解决的类目
* Locking: runtimeLock must be held by the caller
**********************************************************************/
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();
// Methodizing for the first time
if (PrintConnecting) {
_objc_inform("CLASS: methodizing class '%s' %s",
cls->nameForLogging(), isMeta ? "(meta)" : "");
}
// Install methods and properties that the class implements itself.安装类自身实现的方法和属性。
method_list_t *list = ro->baseMethods();
if (list) {
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添加属性列表
rwe->properties.attachLists(&proplist, 1);
}
protocol_list_t *protolist = ro->baseProtocols;
if (rwe && protolist) {
//给rwe添加协议列表
rwe->protocols.attachLists(&protolist, 1);
}
// Root classes get bonus method implementations if they don't have 如果根类没有额外的方法实现,它们会得到额外的方法实现
// them already. These apply before category replacements.
if (cls->isRootMetaclass()) {
// root metaclass
addMethod(cls, @selector(initialize), (IMP)&objc_noop_imp, "", NO);
}
// Attach categories. 添加分类
if (previously) {
if (isMeta) {
objc::unattachedCategories.attachToClass(cls, previously,
ATTACH_METACLASS);
} else {
// When a class relocates, categories with class methods
// may be registered on the class itself rather than on
// the metaclass. Tell attachToClass to look for those.
objc::unattachedCategories.attachToClass(cls, previously,
ATTACH_CLASS_AND_METACLASS);
}
}
objc::unattachedCategories.attachToClass(cls, cls,
isMeta ? ATTACH_METACLASS : ATTACH_CLASS);
#if DEBUG
// Debug: sanity-check all SELs; log method list contents
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
}
prepareMethodLists方法列表排序,为了查找方法的时候使用二分法根据地址大小进行查找。- 有
rwe的话给rwe添加未动态添加的方法列表,属性列表,协议列表。 - 有分类的话
添加分类。
1.2.1 prepareMethodLists验证
static void
prepareMethodLists(Class cls, method_list_t **addedLists, int addedCount,
bool baseMethods, bool methodsFromBundle, const char *why)
{
runtimeLock.assertLocked();
if (addedCount == 0) return;//添加的列表不能为空
// There exist RR/AWZ/Core special cases for some class's base methods.
// But this code should never need to scan base methods for RR/AWZ/Core:
// default RR/AWZ/Core cannot be set before setInitialized().
// Therefore we need not handle any special cases here.
if (baseMethods) {
ASSERT(cls->hasCustomAWZ() && cls->hasCustomRR() && cls->hasCustomCore());
} else if (cls->cache.isConstantOptimizedCache()) {
cls->setDisallowPreoptCachesRecursively(why);
} else if (cls->allowsPreoptInlinedSels()) {
#if CONFIG_USE_PREOPT_CACHES
SEL *sels = (SEL *)objc_opt_offsets[OBJC_OPT_INLINED_METHODS_START];
SEL *sels_end = (SEL *)objc_opt_offsets[OBJC_OPT_INLINED_METHODS_END];
if (method_lists_contains_any(addedLists, addedLists + addedCount, sels, sels_end - sels)) {
cls->setDisallowPreoptInlinedSelsRecursively(why);
}
#endif
}
// Add method lists to array.
// Reallocate un-fixed method lists.重新分配未固定的方法列表。
// The new methods are PREPENDED to the method list array.
auto ro = (const class_ro_t *)cls->data();//自定义
auto isMeta = ro->flags & RO_META;//自定义
for (int i = 0; i < addedCount; i++) {
method_list_t *mlist = addedLists[i];
ASSERT(mlist);
//自定义
const char *systemClassName = cls ->nonlazyMangledName();
const char *LGPersonName = "LGPerson";
if (strcasecmp(systemClassName, LGPersonName) == 0) {
if (!isMeta) {
printf("自己创建的类的方法:%s-%s",__func__,systemClassName);
for (auto& meth : *mlist) {
const char *name = sel_cname(meth.name());
printf("排序前%s-%p\n",name,meth.name());
}
}
}
// Fixup selectors if necessary 排序
if (!mlist->isFixedUp()) {
fixupMethodList(mlist, methodsFromBundle, true/*sort*/);
}
//自定义
if (strcasecmp(systemClassName, LGPersonName) == 0) {
if (!isMeta) {
printf("自己创建的类的方法:%s-%s",__func__,systemClassName);
for (auto& meth : *mlist) {
const char *name = sel_cname(meth.name());
printf("排序后%s-%p\n",name,meth.name());
}
}
}
}
// If the class is initialized, then scan for method implementations如果类已初始化,则扫描方法实现
// tracked by the class's flags. If it's not initialized yet,由类的标志跟踪。如果它还没有初始化,
// then objc_class::setInitialized() will take care of it.然后objc_class::setInitialized()会处理它。
if (cls->isInitialized()) {
objc::AWZScanner::scanAddedMethodLists(cls, addedLists, addedCount);
objc::RRScanner::scanAddedMethodLists(cls, addedLists, addedCount);
objc::CoreScanner::scanAddedMethodLists(cls, addedLists, addedCount);
}
}
其中我们在方法列表排序前打印下,排序后打印下看下结果
排序前按我们
开发时顺序打印,排序后按方法地址的高低排序。继续看下排序的方法
static void
fixupMethodList(method_list_t *mlist, bool bundleCopy, bool sort)
{
runtimeLock.assertLocked();
ASSERT(!mlist->isFixedUp());
// fixme lock less in attachMethodLists ?
// dyld3 may have already uniqued, but not sorted, the list
if (!mlist->isUniqued()) {
mutex_locker_t lock(selLock);
// Unique selectors in list.
for (auto& meth : *mlist) {
const char *name = sel_cname(meth.name());
meth.setName(sel_registerNameNoLock(name, bundleCopy));
}
}
// Sort by selector address.
// Don't try to sort small lists, as they're immutable.
// Don't try to sort big lists of nonstandard size, as stable_sort
// won't copy the entries properly.
if (sort && !mlist->isSmallList() && mlist->entsize() == method_t::bigSize) {
method_t::SortBySELAddress sorter;
std::stable_sort(&mlist->begin()->big(), &mlist->end()->big(), sorter);
}
// Mark method list as uniqued and sorted.
// Can't mark small lists, since they're immutable.
if (!mlist->isSmallList()) {
mlist->setFixedUp();
}
}
通过方法的地址进行排序的。
2.懒加载和非懒加载分析
2.1 懒加载(消息发送)
之前我们在进行判断当前类的时候,在main函数都执行了。还没有记载类的信息,后面通过搜索和注释发现类的+load方法会在load_images中实现prepare_load_methods方法继续实现realizeClassWithoutSwift就是文章一开始分析的。那么我们开发中,不实现类的+load方法,那它是怎样实现的呢?
直接打断点,查看它的堆栈信息。进入main函数后,先打印了
hello world调用LGPerson初始化后进入实现类。堆栈已经很明显的打印了方法的调用流程。lookUpImpOrForward-->initializeAndMaybeRelock-->realizeClassMaybeSwiftMaybeRelock-->realizeClassWithoutSwift
我们平常项目有很多类,但是不是第一时间使用到,为了节约内存,也为了加快程序启动速度。使用了懒加载,当这个OC类第一次去发送消息时候,进行慢速查找,没有实现这个类的话进入realizeClassWithoutSwift实现这个类的相关信息。
2.2 非懒加载(+load)
实现+load方法走的是非懒加载流程
在dyld中运行初始化程序时进入objc_init,调用注册函数
_dyld_objc_notify_register。其中会调用map_images和load_images.正常情况不实现+load方法,map_images不会实现realizeClassWithoutSwift。load_images会递归查找类和分类的+load方法,有的话就要实现类的数据,会进入map_images-->map_images_nolock-->_read_images-->realizeClassWithoutSwift
3.分类探索
分类也是日常开发常用的,那么它是怎么加载呢?先看下本质。我们创建一个LG Person的分类A
#import "LGPerson+A.h"
@implementation LGPerson (A)
-(void)sleep{
NSLog(@"%s",__func__);
}
-(void)eat{
NSLog(@"%s",__func__);
}
-(void)work{
NSLog(@"%s",__func__);
}
@end
我们用Clang编译器编译下clang -rewrite-objc LGPerson+A.m -o A.cpp得到
分类本质也是结构体 搜索
_category_t
其中值得注意的是2个方法列表,分别是
实例方法列表和类方法列表,而不是像类一样类方法在元类。
方法列表中的方法由
_objc_method作为元素存储的 _objc_method包含:方法名,方法签名,地址组成。
查看一下
也就是我们源码中的
method_t
我们也可以通过查看分类的文档:command + shift + 0搜索category
当然我们去源码搜索
category_t
struct category_t {
const char *name;
classref_t cls;
WrappedPtr<method_list_t, PtrauthStrip> instanceMethods;
WrappedPtr<method_list_t, PtrauthStrip> classMethods;
struct protocol_list_t *protocols;
struct property_list_t *instanceProperties;
// Fields below this point are not always present on disk.
struct property_list_t *_classProperties;
method_list_t *methodsForMeta(bool isMeta) {
if (isMeta) return classMethods;
else return instanceMethods;
}
property_list_t *propertiesForMeta(bool isMeta, struct header_info *hi);
protocol_list_t *protocolsForMeta(bool isMeta) {
if (isMeta) return nullptr;
else return protocols;
}
};
我们可以得出以下结论分类本质一个category_t类型的结构体
- 有2个属性
name类的名字和cls类对象 。 - 2个方法列表,
实例方法列表和类方法列表。 - 一个
协议列表,一个属性列表之前我们在探索类的加载的时候进入类methodizeClass方法。
其中就有判断条件,如果有分类的话进入分类的流程。通过
attachToClass添加到类,才能给外部使用。
void attachToClass(Class cls, Class previously, int flags)
{
runtimeLock.assertLocked();
ASSERT((flags & ATTACH_CLASS) ||
(flags & ATTACH_METACLASS) ||
(flags & ATTACH_CLASS_AND_METACLASS));
auto &map = get();
auto it = map.find(previously);
if (it != map.end()) {
category_list &list = it->second;
if (flags & ATTACH_CLASS_AND_METACLASS) {
int otherFlags = flags & ~ATTACH_CLASS_AND_METACLASS;
//准备分类数据
attachCategories(cls, list.array(), list.count(), otherFlags | ATTACH_CLASS);
attachCategories(cls->ISA(), list.array(), list.count(), otherFlags | ATTACH_METACLASS);
} else {
attachCategories(cls, list.array(), list.count(), flags);
}
map.erase(it);
}
}
核心方法attachCategories中分类的数据准备好,处理好最终通过attachLists加入类中
分类加载主要下面几个步骤:
加载的时机,是否和类一样?有懒加载和非懒加载- attachCategories
准备分类的数据。 - attachLists 将分类
添加到主类。
4.总结
类的加载分为懒加载和非懒加载
- 在调用
load_images中回查询类的+load是否有实现,实现话会实现类的非懒加载方法realizeClassWithoutSwift,其中会对类的方法列表,属性列表等进行赋值。其中方法列表会根据方法地址的高低进行排序,为了后面方法查找的时候通过二分法进行快速查找。 - 懒加载,为了节约内存,也为了提高程序启动速度。当类第一次
发送消息的时候进入慢速查找流程,实现这个类。 - 关于数据的写入比如方法列表属性列表等,
data()在编译的时候已经确定数据类型是class_rw_t,会把它赋值给ro的类型是class_ro_t,rw是包含所有ro的类型的所以可以强转rw->ro. 分类的本质也是category_t的结构体。加载过程也是和类一样分为懒加载和非懒加载,但是分类要添加到主类,这要和主类之间就有了2*2=4种可能,下一篇章要进行探索分类的加载流程。