上一篇我们探索到了readClass函数,只是对函数名字进行了赋值,并没有对ro和rw进行操作,本篇我们就继续探索_read_images函数剩下的调用。
realizeClass的引入
因为我们探索的目的是类的加载,我们先忽略protocol和categories的地方。为了调试代码,我们首先还是先创建一个JSPerson类:
@interface JSPerson : NSObject
@property (nonatomic, strong) NSString *nickName;
- (void)say1;
- (void)say2;
+ (void)sayHappy;
@end
#import "JSPerson.h"
@implementation JSPerson
- (void)say1{
NSLog(@"JSPerson say : %s",__func__);
}
- (void)say2{
NSLog(@"JSPerson say : %s",__func__);
}
+ (void)load{
NSLog(@"load");
}
+ (void)sayHappy{
NSLog(@"JSPerson say : %s",__func__);
}
@end
接下来我们继续看_read_images函数,发现和类相关的地方有两个地方,realize non-lazy classes和realize future classes,我们在两段代码中加入我们调试的代码,为了观察我们自定义类的加载情况:
// Realize non-lazy classes (for +load methods and static instances)
for (EACH_HEADER) {
classref_t const *classlist = hi->nlclslist(&count);
for (i = 0; i < count; i++) {
Class cls = remapClass(classlist[i]);
if (!cls) continue;
///调试代码 确定是我们自定义的类
const char *mangledName = cls->nonlazyMangledName();
const char *customerClassName = "JSPerson";
if (strcmp(mangledName, customerClassName) == 0) {
//打印类名
printf("%s -: non-lazy classes要研究的类: - %s\n",__func__,mangledName);
}
addClassTableEntry(cls);
if (cls->isSwiftStable()) {
if (cls->swiftMetadataInitializer()) {
_objc_fatal("Swift class %s with a metadata initializer "
"is not allowed to be non-lazy",
cls->nameForLogging());
}
// fixme also disallow relocatable classes
// We can't disallow all Swift classes because of
// classes like Swift.__EmptyArrayStorage
}
realizeClassWithoutSwift(cls, nil);
}
}
ts.log("IMAGE TIMES: realize non-lazy classes");
// Realize newly-resolved future classes, in case CF manipulates them
if (resolvedFutureClasses) {
for (i = 0; i < resolvedFutureClassCount; i++) {
Class cls = resolvedFutureClasses[i];
if (cls->isSwiftStable()) {
_objc_fatal("Swift class is not allowed to be future");
}
///调试代码 确定是我们自定义的类
const char *mangledName = cls->nonlazyMangledName();
const char *customerClassName = "JSPerson";
if (strcmp(mangledName, customerClassName) == 0) {
//打印类名
printf("%s -: realize future classes要研究的类: - %s\n",__func__,mangledName);
}
realizeClassWithoutSwift(cls, nil);
cls->setInstancesRequireRawIsaRecursively(false/*inherited*/);
}
free(resolvedFutureClasses);
}
ts.log("IMAGE TIMES: realize future classes");
在两行printf的地方打断点,运行源码程序,看是否执行到断点位置。发现打印了_read_images -: non-lazy classes要研究的类: - JSPerson,代码执行到了non-lazy classes,里面类加载的核心代码在realizeClassWithoutSwift函数,我们继续探索realizeClassWithoutSwift函数。
realizeClassWithoutSwift分析
操作之前的ro
我们从上到下依次阅读代码,定位到auto ro = (const class_ro_t )cls->data();,因为ro是clean Memory里的数据我们比较敏感,我们在图示位置打断点:
使用LLDB调试程序,查看ro里的信息:
(lldb) p ro
(const class_ro_t *) $0 = 0x00000001000080c0
(lldb) p *$0
(const class_ro_t) $1 = {
flags = 0
instanceStart = 8
instanceSize = 16
reserved = 0
= {
ivarLayout = 0x0000000000000000
nonMetaclass = nil
}
name = {
std::__1::atomic<const char *> = "JSPerson" {
Value = 0x0000000100003db0 "JSPerson"
}
}
baseMethodList = 0x0000000100008108
baseProtocols = 0x0000000000000000
ivars = 0x0000000100008170
weakIvarLayout = 0x0000000000000000
baseProperties = 0x0000000100008198
_swiftMetadataInitializer_NEVER_USE = {}
}
(lldb) p $1.baseMethodList
(void *const) $2 = 0x0000000100008108
(lldb) p *$2
(lldb)
通过打印我们发现此时ro里的baseMethodList为空,目前还不清楚什么时候赋值的,我们继续探索。
rw的赋值
接下来就是对rw的赋值,注意rw是dirty Memory
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 {
// 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);
}
isa和superClass赋值
后面的代码就是对isa和superClass的赋值:
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.
///元类 不是non pointer ISA
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 也会是纯的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);
在realizeClassWithoutSwift函数最后会调用methodizeClass,我们下一小节探索methodizeClass。
methodizeClass分析
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)" : "");
}
///调试代码 确定是我们自定义的类
const char *mangledName = cls->nonlazyMangledName();
const char *customerClassName = "JSPerson";
if (strcmp(mangledName, customerClassName) == 0) {
//打印类名
if (!isMeta) {
printf("%s -: non-lazy classes要研究的类: - %s\n",__func__,mangledName);
}
}
// 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->properties.attachLists(&proplist, 1);
}
protocol_list_t *protolist = ro->baseProtocols;
if (rwe && protolist) {
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
我们继续探索,后面执行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.
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*/);
}
}
// 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.
if (cls->isInitialized()) {
objc::AWZScanner::scanAddedMethodLists(cls, addedLists, addedCount);
objc::RRScanner::scanAddedMethodLists(cls, addedLists, addedCount);
objc::CoreScanner::scanAddedMethodLists(cls, addedLists, addedCount);
}
}
核心调用是fixupMethodList函数。
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
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();
}
}
核心的代码是stable_sort,我们分别打印排序前后方法列表,如图示
注意:这里一定要先在realizeClassWithoutSwift判断好是我们要研究的JSPerson类,然后再看打印结果,否则系统类也会有很多打印,影响我们分析。
methodizeClass -: non-lazy classes要研究的类: - JSPerson
****************sort之前 : say1 - 0x100003dda
sort之前 : say2 - 0x100003ddf
sort之前 : nickName - 0x7fff73fb8a1c
sort之前 : setNickName: - 0x7fff73fb8362
****************sort之后 : say1 - 0x100003dda
sort之后 : say2 - 0x100003ddf
sort之后 : setNickName: - 0x7fff73fb8362
sort之后 : nickName - 0x7fff73fb8a1c
通过上面打印结果:
- 排序前:
say1 - 0x100003dda、say2 - 0x100003ddf、nickName - 0x7fff73fb8a1c、setNickName: - 0x7fff73fb8362 - 排序后:
say1 - 0x100003dda、say2 - 0x100003ddf、setNickName: - 0x7fff73fb8362、nickName - 0x7fff73fb8a1c - 排序是根据地址由低到高排序的。
小结
到目前为止,类的加载流程是:_read_images->realizeClassWithoutSwift->methodizeClass->prepareMethodLists->fixupMethodList。
懒加载类和非懒加载类
我们前面探索的其实都是非懒加载的类,懒加载类和非懒加载的类的区别就是是否实现了load方法。
非懒加载
通过上面的分析,我们已经很清楚了,是在_objc_init方法里加载的,也就是程序启动的时候。这也就是为什么load方法过多,会影响我们应用的启动速度。
懒加载类
因为非懒加载类效率低,会影响我们的启动速度,那懒加载类是什么时候加载的呢?我们删掉JSPerson类的load方法,然后在main函数中实例化一个JSPerson实例
int main(int argc, const char * argv[]) {
@autoreleasepool {
JSPerson *p = [JSPerson alloc];
[p say1];
NSLog(@"Hello, World!");
}
return 0;
}
我们首先在main方法里添加断点,执行程序。走到main函数之后,然后再在realizeClassWithoutSwift添加断点:
断点走进来之后我们bt打印调用栈信息:
发现调用是从lookUpImpOrForward开始。
所以我们的结论是懒加载的类是在第一次被使用的时候加载的。
总结
-
非懒加载类:程序运行时加载,
_read_images->realizeClassWithoutSwift->methodizeClass->prepareMethodLists->fixupMethodList。 -
懒加载类:第一次使用时加载,
lookUpImpOrForward->realizeClassWithoutSwift->methodizeClass->prepareMethodLists->fixupMethodList我们开发中经常会写
分类,它是什么时候加载的及加载的流程,我们下一篇再探索。