上篇文章应用程序加载分析了dyld
到main()
函数的大体流程,这篇文章主要分析_objc_init()
到类的处理流程。
1._objc_init()分析
/***********************************************************************
* _objc_init
* Bootstrap initialization. Registers our image notifier with dyld.
* Called by libSystem BEFORE library initialization time
**********************************************************************/
void _objc_init(void)
{
static bool initialized = false;
// 只走一次判断
if (initialized) return;
initialized = true;
// fixme defer initialization until an objc-using image is found?
// 读取影响运行时的环境变量,源码工程中可打印。
environ_init();
// 关于线程key的绑定 比如线程数据的析构函数
tls_init();
// 运行 C++ 静态构造函数。在dyld调用我们的静态构造函数之前,libc会调用 _objc_init(),因此需要自己做
static_init();
// runtime运行时环境初始化,主要是unattachedCategories、allocatedClasses方法
runtime_init();
// 初始化 libObjc的异常处理系统
exception_init();
// 缓存条件初始化
cache_init();
// 启动回调机制,通常不会做什么,因为所有的初始化都是惰性的,但是对于某些进程我们会迫不及待的加载trampolines dylib。
_imp_implementationWithBlock_init();
// 注册回调 map_images 管理文件和动态库中的所有符号(class Protocol selector category) load_image 加载执行load方法
_dyld_objc_notify_register(&map_images, load_images, unmap_image);
#if __OBJC2__
didCallDyldNotifyRegister = true;
#endif
}
环境变量可通过修改源码工程代码打印:
void environ_init(void)
{
....省略
for (size_t i = 0; i < sizeof(Settings)/sizeof(Settings[0]); i++) {
const option_t *opt = &Settings[i];
_objc_inform("%s: %s", opt->env, opt->help);
_objc_inform("%s is set", opt->env);
}
}
打印结果:
OBJC_PRINT_LOAD_METHODS
设置了会打印所有执行+(void)load
方法的类,举个
2._dyld_objc_notify_register方法探究
//
// Note: only for use by objc runtime
// Register handlers to be called when objc images are mapped, unmapped, and initialized.
// Dyld will call back the "mapped" function with an array of images that contain an objc-image-info section.
// Those images that are dylibs will have the ref-counts automatically bumped, so objc will no longer need to
// call dlopen() on them to keep them from being unloaded. During the call to _dyld_objc_notify_register(),
// dyld will call the "mapped" function with already loaded objc images. During any later dlopen() call,
// dyld will also call the "mapped" function. Dyld will call the "init" function when dyld would be called
// initializers in that image. This is when objc calls any +load methods in that image.
//
void _dyld_objc_notify_register(_dyld_objc_notify_mapped mapped,
_dyld_objc_notify_init init,
_dyld_objc_notify_unmapped unmapped);
通过注释可知:
- 仅供
objc 运行时
使用。 - 注册回调,当镜像文件被
mapped--映射--&map_images
、unmapped--取消映射--unmap_image
、init--初始化--load_images
。
先看下map_images
方法,通过源码分析可知其核心方法为map_images
->map_images_nolock
-> _read_images
,源码如下:
/***********************************************************************
* _read_images
* Perform initial processing of the headers in the linked
* list beginning with headerList.
*
* Called by: map_images_nolock
*
* Locking: runtimeLock acquired by map_images
**********************************************************************/
void _read_images(header_info **hList, uint32_t hCount, int totalClasses, int unoptimizedTotalClasses)
{
...
// 1.控制一次性加载条件
if (!doneOnce) {
...
// namedClasses
// Preoptimized classes don't go in this table.
// 4/3 is NXMapTable's load factor
int namedClassesSize =
(isPreoptimized() ? unoptimizedTotalClasses : totalClasses) * 4 / 3;
// 创建哈希表 储存不在共享缓存且已命名的类,不论是否实现,容量为类容量的3/4.
gdb_objc_realized_classes =
NXCreateMapTable(NXStrValueMapPrototype, namedClassesSize);
ts.log("IMAGE TIMES: first time tasks");
...
}
// Fix up @selector references
// 2.修复预编译阶段@selector错乱问题 sel是带地址的字符串
static size_t UnfixedSelectors;
{
mutex_locker_t lock(selLock);
for (EACH_HEADER) {
if (hi->hasPreoptimizedSelectors()) continue;
bool isBundle = hi->isBundle();
// 拿到Mach-O中的静态段__objc_selrefs
SEL *sels = _getObjc2SelectorRefs(hi, &count);
UnfixedSelectors += count;
for (i = 0; i < count; i++) {
const char *name = sel_cname(sels[i]);
// 注册 sel
SEL sel = sel_registerNameNoLock(name, isBundle);
// 如果地址不相同改为一致
if (sels[i] != sel) {
sels[i] = sel;
}
}
}
}
...
// 3、错误混乱的类处理
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;
}
// 从Mach-O中获取静态段__objc_classlist
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;
}
}
}
...
// Fix up remapped classes
// Class list and nonlazy class list remain unremapped.
// Class refs and super refs are remapped for message dispatching.
// 4、修复重映射一些没有被镜像文件加载进来的类 经调试没有进入
if (!noClassesRemapped()) {
for (EACH_HEADER) {
Class *classrefs = _getObjc2ClassRefs(hi, &count);
for (i = 0; i < count; i++) {
remapClassRef(&classrefs[i]);
}
// fixme why doesn't test future1 catch the absence of this?
classrefs = _getObjc2SuperRefs(hi, &count);
for (i = 0; i < count; i++) {
remapClassRef(&classrefs[i]);
}
}
}
...
// 5、修复一些消息
for (EACH_HEADER) {
message_ref_t *refs = _getObjc2MessageRefs(hi, &count);
if (count == 0) continue;
if (PrintVtables) {
_objc_inform("VTABLES: repairing %zu unsupported vtable dispatch "
"call sites in %s", count, hi->fname());
}
for (i = 0; i < count; i++) {
fixupMessageRef(refs+i);
}
}
ts.log("IMAGE TIMES: fix up objc_msgSend_fixup");
...
// 6、当类里面有协议时:readProtocol 读取协议 遍历寻找所有的协议列表添加到protocol_map哈希表
for (EACH_HEADER) {
extern objc_class OBJC_CLASS_$_Protocol;
Class cls = (Class)&OBJC_CLASS_$_Protocol;
ASSERT(cls);
NXMapTable *protocol_map = protocols();
bool isPreoptimized = hi->hasPreoptimizedProtocols();
// Skip reading protocols if this is an image from the shared cache
// and we support roots
// Note, after launch we do need to walk the protocol as the protocol
// in the shared cache is marked with isCanonical() and that may not
// be true if some non-shared cache binary was chosen as the canonical
// definition
if (launchTime && isPreoptimized && cacheSupportsProtocolRoots) {
if (PrintProtocols) {
_objc_inform("PROTOCOLS: Skipping reading protocols in image: %s",
hi->fname());
}
continue;
}
bool isBundle = hi->isBundle();
// 获取到Mach-O中的静态段__objc_protolist协议列表,存入protocol_map表
protocol_t * const *protolist = _getObjc2ProtocolList(hi, &count);
for (i = 0; i < count; i++) {
readProtocol(protolist[i], cls, protocol_map,
isPreoptimized, isBundle);
}
}
...
// Fix up @protocol references
// Preoptimized images may have the right
// answer already but we don't know for sure.
//7、修复没有被加载的协议
for (EACH_HEADER) {
// At launch time, we know preoptimized image refs are pointing at the
// shared cache definition of a protocol. We can skip the check on
// launch, but have to visit @protocol refs for shared cache images
// loaded later.
if (launchTime && cacheSupportsProtocolRoots && hi->isPreoptimized())
continue;
// 获取到Mach-O的静态段 __objc_protorefs
protocol_t **protolist = _getObjc2ProtocolRefs(hi, &count);
for (i = 0; i < count; i++) {
// 比较协议内存地址是否相同,不同则替换
remapProtocolRef(&protolist[i]);
}
}
...
// 8、分类处理
// Discover categories. Only do this after the initial category
// attachment has been done. For categories present at startup,
// discovery is deferred until the first load_images call after
// the call to _dyld_objc_notify_register completes. rdar://problem/53119145
// 需要在分类初始化将数据加载到类后才执行,对于运行时出现的分类,将分类的发现推迟到对_dyld_objc_notify_register调用完成后地方第一个load_images调用为止
if (didInitialAttachCategories) {
for (EACH_HEADER) {
load_categories_nolock(hi);
}
}
...
// Category discovery MUST BE Late to avoid potential races
// when other threads call the new category code before
// this thread finishes its fixups.
// +load handled by prepare_load_methods()
// Realize non-lazy classes (for +load methods and static instances)
// 9、类的加载处理
for (EACH_HEADER) {
// 获取Mach-O的静态段__objc_nlclslist非懒加载类表
classref_t const *classlist =
_getObjc2NonlazyClassList(hi, &count);
for (i = 0; i < count; i++) {
Class cls = remapClass(classlist[i]);
if (!cls) continue;
// 插入当前类
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
}
// 实现类上部分的cls为类名+地址但是类数据并没初始化。
realizeClassWithoutSwift(cls, nil);
}
}
...
// 10、没有被处理的类,优化那些被侵犯的类
// 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");
}
// 实现类
realizeClassWithoutSwift(cls, nil);
cls->setInstancesRequireRawIsaRecursively(false/*inherited*/);
}
free(resolvedFutureClasses);
}
ts.log("IMAGE TIMES: realize future classes");
if (DebugNonFragileIvars) {
// 实现所有类
realizeAllClasses();
}
...
}
第三步的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->mangledName();
//当前类的父类中若有丢失的weak-linked类,则返回nil
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->superclass = nil;
return nil;
}
cls->fixupBackwardDeployingStableSwift();
Class replacing = nil;
// 判断是否是未来要处理的类(断点后发现不会走)
if (Class newCls = popFutureNamedClass(mangledName)) {
... ro rw处理
}
// 判断类是否已经加载到内存
if (headerIsPreoptimized && !replacing) {
// class list built in shared cache
// fixme strict assert doesn't work because of duplicates
// ASSERT(cls == getClass(name));
ASSERT(getClassExceptSomeSwift(mangledName));
} else {
addNamedClass(cls, mangledName, replacing); // 加载到共享缓存中
addClassTableEntry(cls); // 插入表 machO-->内存
}
// 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;
}
mangledName
方法源码:
const char *mangledName() {
// fixme can't assert locks here
ASSERT(this);
// 已经实现或者未来类,类名从rw->ro()->name
if (isRealized() || isFuture()) {
return data()->ro()->name;
} else {
//从ro中获取
return ((const class_ro_t *)data())->name;
}
}
addNamedClass
源码分析:
/***********************************************************************
* addNamedClass
* Adds name => cls to the named non-meta class map.
* Warns about duplicate class names and keeps the old mapping.
* Locking: runtimeLock must be held by the caller
**********************************************************************/
static void addNamedClass(Class cls, const char *name, Class replacing = nil)
{
runtimeLock.assertLocked();
Class old;
if ((old = getClassExceptSomeSwift(name)) && old != replacing) {
inform_duplicate(name, old, cls);
// getMaybeUnrealizedNonMetaClass uses name lookups.
// Classes not found by name lookup must be in the
// secondary meta->nonmeta table.
addNonMetaClass(cls);
} else {
// 插入到gdb_objc_realized_classes哈希表 在runtimeinit()方法中创建
NXMapInsert(gdb_objc_realized_classes, name, cls);
}
ASSERT(!(cls->data()->flags & RO_META));
// wrong: constructed classes are already realized when they get here
// ASSERT(!cls->isRealized());
}
获取到类名之后把类名插入gdb_objc_realized_classes
哈希表。
readClass总结: 只是把类从machO
取出读到内存,插入表中,获取到的类为地址 + 名字
。
realizeClassWithoutSwift 方法
realizeClassWithoutSwift
主要是下面三个步骤:
1.处理data(),将data()---ro
替换为rw
,并把ro
拷贝到rw -->ro
2.递归调用realizeClassWithoutSwift
完善类结构和继承连关系。
3.methodizeClass
处理方法。
1 处理data()数据
static Class realizeClassWithoutSwift(Class cls, Class previously)
{
runtimeLock.assertLocked();
class_rw_t *rw;
Class supercls;
Class metacls;
if (!cls) return nil;
if (cls->isRealized()) return cls; // 如果是已实现类(data()读出来是rw) 直接返回
ASSERT(cls == remapClass(cls));
// fixme verify class is not in an un-dlopened part of the shared cache?
// 取出类data()此时为class_ro_t
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 {
// Normal class. Allocate writeable class data.
rw = objc::zalloc(); //rw分配空间
rw->set_ro(ro); //设置rw中的ro
rw->flags = RW_REALIZED|RW_REALIZING|isMeta; // 设置flag RW_REALIZED class_t->data 是rw非ro RW_REALIZING 类已经实现但未完成
cls->setData(rw); //data()设置为rw
}
...
-
ro
为readOnly
在编译时确定了内存,包含类名、方法、协议、和实例变量等信息,由于是只读的,被称为clean Memory
,值加载后不会发生改变的内存。 -
rw
表示readWrite
,由于动态性,可能会往类中添加属性,方法、协议,在2020WWDC
中关于内存优化
提到rw
,只有10%的类改变了rw
,所以有的rwe
,类的额外可读可写结构,rw
为可读可写,所有为dirty Memory
,运行时可能会添加方法或者属性等。
2.递归调用 realizeClassWithoutSwift 完善类结构和继承连关系。
// 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.
// 递归调用 确定继承连关系 实现父类和元类 remapClass中当找到NSObject返回nil打破循环
supercls = realizeClassWithoutSwift(remapClass(cls->superclass), nil);
// 根元类isa指向自身 通过if (cls->isRealized()) return cls 打破循环。
metacls = realizeClassWithoutSwift(remapClass(cls->ISA()), nil);
...
// 完善类结构 赋值父类和isa指向元类
cls->superclass = supercls;
cls->initClassIsa(metacls);
....
// Connect this class to its superclass's subclass lists
if (supercls) {
// 双向绑定父类子类关系
addSubclass(supercls, cls);
} else {
// 绑定NSObjcet或者别的没有父类的根类,父类指向nil
addRootClass(cls);
}
....
递归调用realizeClassWithoutSwift
循环设置父类
和元类
类,经测试最先走的类是NSObject
元类,然后是NSObject
类。
static Class remapClass(Class cls)
{
runtimeLock.assertLocked();
if (!cls) return nil;
auto *map = remappedClasses(NO);
if (!map)
return cls;
auto iterator = map->find(cls);
if (iterator == map->end())
return cls;
return std::get<1>(*iterator);
}
3.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.
// 添加方法列表、属性列表、协议列表到rw中
// 方法列表
method_list_t *list = ro->baseMethods();
if (list) {
prepareMethodLists(cls, &list, 1, YES, isBundleClass(cls)); //根据sel地址,对方法列表排序
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 根元类添加initialize方法
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);
...
}
methodizeClass
方法主要是为类
添加方法、属性、协议,当当前调用者为类的时候,只会对rw->ro()
的方法进行排序(按照sel
地址),此时取出的是ro
,不是rwe
。
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
,添加分类信息,分为+
和-
方法。
// Attach method lists and properties and protocols from categories to a class.
// Assumes the categories in cats are all loaded and sorted by load order,
// oldest categories first.
static void
attachCategories(Class cls, const locstamped_category_t *cats_list, uint32_t cats_count,
int flags)
{
if (slowpath(PrintReplacedMethods)) {
printReplacements(cls, cats_list, cats_count);
}
if (slowpath(PrintConnecting)) {
_objc_inform("CLASS: attaching %d categories to%s class '%s'%s",
cats_count, (flags & ATTACH_EXISTING) ? " existing" : "",
cls->nameForLogging(), (flags & ATTACH_METACLASS) ? " (meta)" : "");
}
/*
* Only a few classes have more than 64 categories during launch.
* This uses a little stack, and avoids malloc.
*
* Categories must be added in the proper order, which is back
* to front. To do that with the chunking, we iterate cats_list
* from front to back, build up the local buffers backwards,
* and call attachLists on the chunks. attachLists prepends the
* lists, so the final result is in the expected order.
*/
constexpr uint32_t ATTACH_BUFSIZ = 64;
// 二维数组
method_list_t *mlists[ATTACH_BUFSIZ];
property_list_t *proplists[ATTACH_BUFSIZ];
protocol_list_t *protolists[ATTACH_BUFSIZ];
uint32_t mcount = 0;
uint32_t propcount = 0;
uint32_t protocount = 0;
bool fromBundle = NO;
bool isMeta = (flags & ATTACH_METACLASS);
// ⚠️ 如果rew没有,初始化rwe 并把ro中的方法、属性、协议列表储存到rwe中
auto rwe = cls->data()->extAllocIfNeeded();
for (uint32_t i = 0; i < cats_count; i++) {
auto& entry = cats_list[i];
method_list_t *mlist = entry.cat->methodsForMeta(isMeta);
if (mlist) {
if (mcount == ATTACH_BUFSIZ) {
prepareMethodLists(cls, mlists, mcount, NO, fromBundle); //排序
rwe->methods.attachLists(mlists, mcount);
mcount = 0;
}
//倒叙插入
mlists[ATTACH_BUFSIZ - ++mcount] = mlist;
fromBundle |= entry.hi->isBundle();
}
property_list_t *proplist =
entry.cat->propertiesForMeta(isMeta, entry.hi);
if (proplist) {
if (propcount == ATTACH_BUFSIZ) {
rwe->properties.attachLists(proplists, propcount);
propcount = 0;
}
proplists[ATTACH_BUFSIZ - ++propcount] = proplist;
}
protocol_list_t *protolist = entry.cat->protocolsForMeta(isMeta);
if (protolist) {
if (protocount == ATTACH_BUFSIZ) {
rwe->protocols.attachLists(protolists, protocount);
protocount = 0;
}
protolists[ATTACH_BUFSIZ - ++protocount] = protolist;
}
}
if (mcount > 0) {
prepareMethodLists(cls, mlists + ATTACH_BUFSIZ - mcount, mcount, NO, fromBundle);
rwe->methods.attachLists(mlists + ATTACH_BUFSIZ - mcount, mcount);
if (flags & ATTACH_EXISTING) flushCaches(cls);
}
rwe->properties.attachLists(proplists + ATTACH_BUFSIZ - propcount, propcount);
rwe->protocols.attachLists(protolists + ATTACH_BUFSIZ - protocount, protocount);
}
⚠️auto rwe = cls->data()->extAllocIfNeeded()
,rwe
是在这个方法初始化的,因为这里要对ro
的clean memory
进行处理了,并把ro
中的方法、属性、协议列表赋值到rwe
中,全局搜索cls->data()->extAllocIfNeeded()
发现除了这里还有_class_addProperty
、class_addProtocol
、addMethods
三个方法进行调用,原理相同就是要改变ro
本来的clean memory
的时候才会开辟。
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;
// 开辟新数组
setArray((array_t *)realloc(array(), array_t::byteSize(newCount)));
array()->count = newCount;
// 向后移动要加入的数量位
memmove(array()->lists + addedCount, array()->lists,
oldCount * sizeof(array()->lists[0]));
//memcpy 从什么位置开始拷贝什么 放多大
memcpy(array()->lists, addedLists,
addedCount * sizeof(array()->lists[0]));
}
else if (!list && addedCount == 1) {
// 0 lists -> 1 list
list = addedLists[0];
}
else {
// 1 list -> many lists
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;
// oldlist放最后
if (oldList) array()->lists[addedCount] = oldList;
//memcpy 从开始位置开始放入addedLists,
memcpy(array()->lists, addedLists,
addedCount * sizeof(array()->lists[0]));
}
}
attachLists
方法主要是为methods
、properties
、protocols
二维数组赋值.
- list = addedLists[0] 为
0-1
过程。 - 1 list -> many lists 为
1-多
过程,先将旧数组放到最后位置,然后从位置0
插入新数据。 - many lists -> many lists 为多对多过程,先将旧数组后移
要添加的数量
位置,然后从位置0
插入新数据。
分类加载
1.定义一个类,两个分类,有同名方法
@interface Person : NSObject
-(void)say;
@end
@implementation Person
- (void)say
{
}
//+ (void)load
//{
// NSLog(@" %s", __func__);
//}
//////////////////////////////////////////
@interface Person (e1)
-(void)addmethod2;
-(void)addmethod1;
@end
-(void)addmethod2
{
}
-(void)addmethod1
{
}
//
//+ (void)load
//{
// NSLog(@" %s", __func__);
//}
-(void)say
{
NSLog(@"e1 %s", __func__);
}
//////////////////////////////////////////
@interface Person (e2)
-(void)aaaaaaa;
@end
@implementation Person (e2)
//+ (void)load
//{
// NSLog(@" %s", __func__);
//// method_exchangeImplementations(class_getInstanceMethod(self, @selector(say)), class_getInstanceMethod(self, @selector(say2)));
//}
-(void)aaaaaaa
{
}
-(void)say
{
NSLog(@"e2 %s", __func__);
}
main函数
int main(int argc, const char * argv[]) {
@autoreleasepool {
// insert code here...
Person *p =[Person alloc];
[p say];
}
return 0;
}
2.运行查看堆栈信息。
- 1.都不实现load方法
从打印堆栈顺序可知,先执行的 main.m
中的[p say]
,开始方法的慢速查找,realizeClassWithoutSwift
实现该类,ro->baseMethods()
取出的list
已经包括了所有的方法,来打印下:
包括声明的所有方法,但是是没有顺序的。往下一步排序:
已经是排序好的方法。
- 2.主类实现load方法,分类不实现
从堆栈调用信息可知,此流程通过map_images
->_read_images
实现类,在main
方法之前调用,通过刚才总结的_read_images
的第九步实现类。
`ro->baseMethods()`取出的`list`已经包括了所有的方法,后续步骤与第一步相同,只是前面执行的时机不相同。
- 3.主类实现load方法,分类实现load方法
通过方法打印可知先调用的map_images
->_read_images
实现类,然后通过load_images
->loadAllCategories
-...-->attachCategories
添加分类数据,在此时创建rwe
,把分类方法经过排序后放进rwe->methods
中,attachLists
方法走一对多过程。 - 4.主类不实现load方法,分类实现load方法
通过堆栈打印信息可知,通过load_images
->prepare_load_methods
->realizeClassWithoutSwift
先实现本类。
通过unattachedCategories.attachToClass
->attachCategories
添加分类方法,rwe
在attachCategories
方法中创建,attachLists
添加分类方法走1 list -> many lists
过程。