回顾
在上篇类的加载原理分析(上)我们主要是对_read_images函数到底做了哪些事情,进行了一个大致的分析,也在这个过程中找到了接下来探索的思路,也就是在readClass中通过判断类名从而筛选出我们自定义的类是如何加载的,当然大家也可以在那里进行断点调式,打印cls的类的信息,这个在之前的篇章类的原理分析有介绍类的结构,只需要按照结构就可以判断当前cls是否已经完成了ro,rw,rwe的过程,这也是我们之后定位cls加载完成的关键
一.realizeClassWithoutSwift引入
首先在_read_images中第9,10部分添加调式代码,因为这也是我们需要关注的地方,虽然通过注释也能大致猜出来,但是抱着严谨求学的态度,还是要测试一下,如图
1.类的加载处理 类实现
2.没有被处理的类 优化那些被侵犯的类
相信大家和我一样,我会认为肯定会走近断点处把😄,跑起来!!!
int main(int argc, const char * argv[]) {
@autoreleasepool {
[LGPerson sayHappy];
NSLog(@"Hello, World!");
}
return 0;
}
运行结果
OhMyGod!!!没有断进来。。。添加代码的地方都没走哦。。。
为啥呢,喝口水,压压惊😄 再试一次!!!
然后进来了。。。
计算机走一波:666😄
为什么2次执行结果不一样呢?答案就在这里
因为我给LGperson类添加了+load方法,那么为什么会这样呢?
问题1:如果我不添加load方法,那么这个类的实例方法与类方法在什么时候加载呢?
接下继续往下走就进入了realizeClassWithoutSwift
1.realizeClassWithoutSwift
/***********************************************************************
* 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);
return cls;
}
ASSERT(cls == remapClass(cls));
// 验证类不在共享缓存的un-dlopened 部分?
// fixme verify class is not in an un-dlopened part of the shared cache?
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 = cls->data();
ro = cls->data()->ro();
ASSERT(!isMeta);
cls->changeInfo(RW_REALIZED|RW_REALIZING, RW_FUTURE);
} else {//普通类 分配可写的类数据 rw的写入
// Normal class. Allocate writeable class data. 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
// Choose an index for this class.
// Sets cls->instancesRequireRawIsa if indexes no more indexes are available
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.
// 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.
// fixme that assumption will be wrong if we add support
// for ObjC subclasses of Swift classes.
supercls = realizeClassWithoutSwift(remapClass(cls->getSuperclass()), nil);
metacls = realizeClassWithoutSwift(remapClass(cls->ISA()), nil);
#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
// Non-pointer isa disabled by environment or app SDK version
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;
}
通过最后的函数methodizeClass去附加类别,可以看到类的rw写入就是在这里执行的,而且在下面有这2句代码:
supercls = realizeClassWithoutSwift(remapClass(cls->getSuperclass()), nil);
metacls = realizeClassWithoutSwift(remapClass(cls->ISA()), nil);
可以看出这里是递归获取的cls关联类的信息,全部都要设置rw返回,参考isa走位图也能明白之间的执行顺序,从当前类->父类->元类->依次类推
2.methodizeClass
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);
}
。。。。。。。
}
通过上面的注释可以看出从这里开始就是给类添加自身的属性方法了
3.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;
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
}
//将方法列表添加到数组。
//重新分配不固定的方法列表。
//新方法在方法列表数组前面。
//说白了排序
for (int i = 0; i < addedCount; i++) {
method_list_t *mlist = addedLists[i];
ASSERT(mlist);
// Fixup selectors if necessary 修复选择器
if (!mlist->isFixedUp()) {
fixupMethodList(mlist, methodsFromBundle, true/*sort*/);
}
}
//如果类已经初始化,就扫描实现方法,没有就objc::CoreScanner::scanAddedMethodLists处理掉
if (cls->isInitialized()) {
objc::AWZScanner::scanAddedMethodLists(cls, addedLists, addedCount);
objc::RRScanner::scanAddedMethodLists(cls, addedLists, addedCount);
objc::CoreScanner::scanAddedMethodLists(cls, addedLists, addedCount);
}
}
4.fixupMethodList
修复选择器
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
//在附件方法列表中修复较少的锁?
//dyld3可能已经唯一化了列表,但没有排序
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);
//按照选择器地址排序,给sel排序 按照地址排序
}
// Mark method list as uniqued and sorted.
// Can't mark small lists, since they're immutable.
if (!mlist->isSmallList()) {
mlist->setFixedUp();
}
}
通过注释可以看出这里是中间的method_t::SortBySELAddress sorter是进行排序的关键,那么这里我们可以做一下验证,看下方法的顺序是否真的有所调整?代码如下
在排序前和排序后分别打印方法名称即可
但是千万不要忘了,在fixupMethodList函数之前的某个地方,要先断点筛选出当前加载的类是我们自定义类才行哦,像methodizeClass,readClass等函数都行,然后就是给类添加属性和方法,代码如下
int main(int argc, const char * argv[]) {
@autoreleasepool {
[LGPerson sayHappy];
LGPerson *p = [LGPerson alloc];
[p cate_instanceMethod2];
NSLog(@"Hello, World!");
}
return 0;
}
可以看到后面的顺序发生了改变,是按照地址排序的
可是也有个疑问2:就是属性生成的setter,getter方法的地址好像会变化,而且通过测试发现对象方法也就是存在类的方法,在这里的顺序是排好的,前后是一样的顺序,但是元类也就是类方法的顺序确实是发生了变化,所以暂时得出的结论是:
类的方法顺序并没有在这里发生变化,因为排序之前的地址已经排好序了,但是也验证了fixupMethodList函数的排序功能
二.懒加载类和非懒加载类
通过上面的测试也可以看出:普通类的实现里面加了 +load方法 ,该类就是非懒加载类,不然就是懒加载类
其实懒加载类的方法查找流程,我们在之前的篇章其实就走过,也就是在类第一次发送消息的时候去加载类的信息时,会进行消息的慢速查找(lookUpImpOrForward),篇章:消息的快速慢速查找
而非懒加载类则是dyld加载应用程序的时map_images加载所有非懒加载类的数据(未完成)