Category原理
带着问题出发
- Category的结构了解吗,说来听听??
- Category的作用是什么,你在项目中用到那些,为什么要这样做。
- Category里面的同名方法为什么会覆盖Class里面的方法,原理是什么,
- Category是如何合并到Class上的,什么时候合并的,合并执行了那些操作,attachCategories 的步骤是怎么样的,了解attachLists吗
- Category存放在哪里,对应mach-o文件的那个段
- 为什么Category不能存储成员变量,为什么?
- Category与Extension的区别,如何去验证
- load方法与initialize的区别,存在继承关系又是如何?是父类的先执行还是什么,没有继承关系的顺序又是怎么样的??
- initialize方法内部是如何是如何实现的,调用顺序是怎样的 ,子类不存在initialize方法,父类的initialize方法为什么会多次执行。
- 为什么要设计Load这个方法???
- 如何统计Load方法的耗时时间
执行代码
注意Person+Test1是Person 的一个分类。
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc Person+Test1.m -o Person+Test1-arm64.cpp
Person+Test1生成的部分代码示例
_category_t _OBJC_$_CATEGORY_Person_$_Test1结构
static struct _category_t _OBJC_$_CATEGORY_Person_$_Test1 __attribute__ ((used, section ("__DATA,__objc_const"))) =
{
"Person",
&OBJC_CLASS_$_Person,
(const struct _method_list_t *)&_OBJC_$_CATEGORY_INSTANCE_METHODS_Person_$_Test1,
(const struct _method_list_t *)&_OBJC_$_CATEGORY_CLASS_METHODS_Person_$_Test1,
0,
0,
};
instance method
static struct /*_method_list_t*/ {
unsigned int entsize; // sizeof(struct _objc_method)
unsigned int method_count;
struct _objc_method method_list[4];
} _OBJC_$_CATEGORY_INSTANCE_METHODS_Person_$_Test1 __attribute__ ((used, section ("__DATA,__objc_const"))) = {
sizeof(_objc_method),
4,
{{(struct objc_selector *)"test", "v16@0:8", (void *)_I_Person_Test1_test},
{(struct objc_selector *)"test1", "v16@0:8", (void *)_I_Person_Test1_test1},
{(struct objc_selector *)"test2", "v16@0:8", (void *)_I_Person_Test1_test2},
{(struct objc_selector *)"testProtocolRun", "v16@0:8", (void *)_I_Person_Test1_testProtocolRun}}
};
class method
static struct /*_method_list_t*/ {
unsigned int entsize; // sizeof(struct _objc_method)
unsigned int method_count;
struct _objc_method method_list[2];
} _OBJC_$_CATEGORY_CLASS_METHODS_Person_$_Test1 __attribute__ ((used, section ("__DATA,__objc_const"))) = {
sizeof(_objc_method),
2,
{{(struct objc_selector *)"load", "v16@0:8", (void *)_C_Person_Test1_load},
{(struct objc_selector *)"initialize", "v16@0:8", (void *)_C_Person_Test1_initialize}}
};
category_t 结构
struct category_t {
// 名称
const char *name;
// isa
classref_t cls;
//对象方法
struct method_list_t *instanceMethods;
//类方法
struct method_list_t *classMethods;
//协议列表
struct protocol_list_t *protocols;
//实例属性列表
struct property_list_t *instanceProperties;
//class属性列表
struct property_list_t *_classProperties;
// note: 元类中保存类方法
method_list_t *methodsForMeta(bool isMeta) {
if (isMeta) return classMethods;
else return instanceMethods;
}
// 区分类对象,元类对象
propertiesForMeta(bool isMeta, struct header_info *hi){
if (!isMeta) return instanceProperties;
else if (hi->info()->hasCategoryClassProperties()) return _classProperties;
else return nil;
}
};
方法执行顺序
dyld
-> libSystem.B.dylib : libSystem_initializer
-> libdispatch.dylib:libdispatch_init
-> libdispatch.dylib:_os_object_init
-> libobjc.A.dylib`:_objc_init()
_objc_init 方法
void _objc_init(void)
{
static bool initialized = false;
if (initialized) return;
initialized = true;
// note: 处理链接库中的类
// 注册通知:当一个加载镜像image的状态发生改变,将执行对应的方法。
_dyld_objc_notify_register(&map_images, load_images, unmap_image);
}
dyld中注册通知
void _dyld_objc_notify_register(_dyld_objc_notify_mapped mapped,
_dyld_objc_notify_init init,
_dyld_objc_notify_unmapped unmapped)
{
//赋值操作
sNotifyObjCMapped = mapped;
sNotifyObjCInit = init;
sNotifyObjCUnmapped = unmapped;
}
需要注意的是map_images,load_images会执行多次。【unmapped没有实践】
- 发送map_images通知
static void notifyBatchPartial(dyld_image_states state....){
if ((state == dyld_image_state_bound) || (orLater && (dyld_image_state_bound > state))) )
{
//告知objc执行map_images方法
(*sNotifyObjCMapped)(objcImageCount, paths, mhs);
}
}
- 发送load_images通知
当state == dyld_image_state_dependents_initialized时候
if (state == dyld_image_state_dependents_initialized) {
*sNotifyObjCInit(image->getRealPath(), image->machHeader());
}
- 发送 unmap_image通知
if ( image->getState() >= dyld_image_state_bound ) {
if ( sNotifyObjCUnmapped != NULL && image->notifyObjC() )
(*sNotifyObjCUnmapped)(image->getRealPath(), image->machHeader());
}
map_images方法
关键方法调用顺序
map_images()
└── map_images_nolock()
└──read_images()
└──realizeClass()
└──methodizeClass()
└──attachCategories()
└── prepareMethodLists()
map_images_nolock()方法
读取mhdrs[mhCount]中全部class的数量。
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;
hCount = 0;
// 读取mach-o里面的全部类的totalClasses数量,以及不在共享缓存中的unoptimizedTotalClasses
int totalClasses = 0;
int unoptimizedTotalClasses = 0;
{
uint32_t i = mhCount;
while (i--) {
const headerType *mhdr = (const headerType *)mhdrs[i];
//读取mach_header[i]中的class全部数量
auto hi = addHeader(mhdr, mhPaths[i], totalClasses, unoptimizedTotalClasses);
if (!hi) {
continue;
}
hList[hCount++] = hi;
}
}
if (hCount > 0) {
_read_images(hList, hCount, totalClasses, unoptimizedTotalClasses);
}
}
_read_images()方法
NXMapTable *gdb_objc_realized_classes初始化,里面存放[className:class]- 修复需要realloc的Class为newCls,将信息存入到
NXMapTable *remapped_class_map里面,remapped_class_map不为空,循环遍历修改。 - 注册所有 selector,内存唯一化,存储到
NXMapTable *namedSelectors中。 - 初始化所有 protocol_t。
- 处理含有load方法的class,进行realizeClass()方法调用。
- 为重新分配内存指向的Class,执行realizeClass
- 记录所有 image 中待处理的 category,在 realizeClass 的时候处理。
void _read_images(header_info **hList, uint32_t hCount, int totalClasses, int unoptimizedTotalClasses)
{
// 1.初始化gdb_objc_realized_classes ,存放key=className:Class
static bool doneOnce;
if (!doneOnce) {
doneOnce = YES;
gdb_objc_realized_classes =
NXCreateMapTable(NXStrValueMapPrototype, namedClassesSize);
}
// 2. 修复需要reallc的class
for (EACH_HEADER) {
//判断是否需要提前要执行readClass()方法
if (! mustReadClasses(hi)) {
// Image is sufficiently optimized that we need not call readClass()
continue;
}
bool headerIsBundle = hi->isBundle();
bool headerIsPreoptimized = hi->isPreoptimized();
//note: 当前 mach-o 中定义的所有类列表classlist
classref_t *classlist = _getObjc2ClassList(hi, &count);
for (i = 0; i < count; i++) {
Class cls = (Class)classlist[i];
//判断cls是否要转变为newCls,将cls的rw信息拷贝到newCls.rw,newCls.ro中。将信息存入到NXMapTable *remapped_class_map【注意这里还没有合并Category中的方法】
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
resolvedFutureClasses = (Class *)
realloc(resolvedFutureClasses,
(resolvedFutureClassCount+1) * sizeof(Class));
//记录要重新分配的class,resolvedFutureClasses是个一维数组
resolvedFutureClasses[resolvedFutureClassCount++] = newCls;
}
}
}
// 修复需要realloc的Class为新newCls
// 判断 NXMapTable *remapped_class_map是否为空
if (!noClassesRemapped()) {
for (EACH_HEADER) {
//代码中用到的类引用
Class *classrefs = _getObjc2ClassRefs(hi, &count);
for (i = 0; i < count; i++) {
// 用于处理 CoreFoundation toll-bridging 的类,在标为 future class 后会 realloc,因此需要指向最新的内存
remapClassRef(&classrefs[i]);
}
// fixme why doesn't test future1 catch the absence of this?
//note: 代码中用到的 superclass 引用
classrefs = _getObjc2SuperRefs(hi, &count);
for (i = 0; i < count; i++) {
remapClassRef(&classrefs[i]);
}
}
}
// 3.注册所有 selector,内存唯一化
static size_t UnfixedSelectors;
sel_lock();
for (EACH_HEADER) {
if (hi->isPreoptimized()) continue;
bool isBundle = hi->isBundle();
//代码中用到的 selector 引用
SEL *sels = _getObjc2SelectorRefs(hi, &count);
UnfixedSelectors += count;
for (i = 0; i < count; i++) {
const char *name = sel_cname(sels[i]);
sels[i] = sel_registerNameNoLock(name, isBundle);
}
}
sel_unlock();
// 4.初始化所有 protocol_t
for (EACH_HEADER) {
extern objc_class OBJC_CLASS_$_Protocol;
Class cls = (Class)&OBJC_CLASS_$_Protocol;
assert(cls);
NXMapTable *protocol_map = protocols();
bool isPreoptimized = hi->isPreoptimized();
bool isBundle = hi->isBundle();
// note: 当前 mach-o 中定义的所有 protocol 列表
protocol_t **protolist = _getObjc2ProtocolList(hi, &count);
for (i = 0; i < count; i++) {
readProtocol(protolist[i], cls, protocol_map,
isPreoptimized, isBundle);
}
}
// Fix up @protocol references
// note: 处理所有 protocol_t *,处理 protocol_t 内存被重新创建的情况,让指针指向最新的内存
// Preoptimized images may have the right
// answer already but we don't know for sure.
for (EACH_HEADER) {
//// note: 代码中用到的 protocol 引用
protocol_t **protolist = _getObjc2ProtocolRefs(hi, &count);
for (i = 0; i < count; i++) {
//上一步的readProtocol可能修改了isa
remapProtocolRef(&protolist[i]);
}
}
// 5.处理含有load方法的class,进行realizeClass()处理。
for (EACH_HEADER) {
//note: 包含 +load 方法的类
classref_t *classlist =
_getObjc2NonlazyClassList(hi, &count);
for (i = 0; i < count; i++) {
// Future Class
Class cls = remapClass(classlist[i]);
if (!cls) continue;
realizeClass(cls);
}
}
// Realize newly-resolved future classes, in case CF manipulates them
// CF库中要处理的future classes,执行realizeClass方法
// 6.为重新分配内存指向的Class,执行realizeClass
if (resolvedFutureClasses) {
for (i = 0; i < resolvedFutureClassCount; i++) {
realizeClass(resolvedFutureClasses[i]);
resolvedFutureClasses[i]->setInstancesRequireRawIsa(false/*inherited*/);
}
free(resolvedFutureClasses);
}
// 7. 记录所有 image 中待处理的 category,在 realizeClass 的时候处理。
for (EACH_HEADER) {
// note: 当前 mach-o 中定义的所有 category 列表
category_t **catlist =
_getObjc2CategoryList(hi, &count);
bool hasClassProperties = hi->info()->hasCategoryClassProperties();
for (i = 0; i < count; i++) {
category_t *cat = catlist[i];
Class cls = remapClass(cat->cls);
if (!cls) {
catlist[i] = nil;
continue;
}
bool classExists = NO;
if (cat->instanceMethods || cat->protocols
|| cat->instanceProperties)
{
addUnattachedCategoryForClass(cat, cls, hi);
}
if (cat->classMethods || cat->protocols
|| (hasClassProperties && cat->_classProperties))
{
addUnattachedCategoryForClass(cat, cls->ISA(), hi);
}
}
}
}
realizeClass方法
- 创建rw数据,rw.ro = ro
- 初始化父类,元类
- non-pointer 处理
- 重对齐实例变量偏移地址
- 设置类之间的树形关系,方便之后遍历
- 执行methodizeClass方法,将ro里面的method,protocol,property复制一份到rw里面
static Class realizeClass(Class cls)
{
const class_ro_t *ro;
class_rw_t *rw;
Class supercls;
Class metacls;
bool isMeta;
//1. 处理rw,不存在的重新生成rw
ro = (const class_ro_t *)cls->data();
// future class,rw数据已经分配过
if (ro->flags & RO_FUTURE) {
// This was a future class. rw data is already allocated.
rw = cls->data();
ro = cls->data()->ro;
cls->changeInfo(RW_REALIZED|RW_REALIZING, RW_FUTURE);
} else {
// Normal class. Allocate writeable class data.
// note: 为 rw 创建内存
rw = (class_rw_t *)calloc(sizeof(class_rw_t), 1);
rw->ro = ro;
//修改为已经初始化了
rw->flags = RW_REALIZED|RW_REALIZING;
cls->setData(rw);
}
isMeta = ro->flags & RO_META;
// 2.初始化父类,元类
// note: 首先初始化父类
supercls = realizeClass(remapClass(cls->superclass));
// note: 首先初始化元类
metacls = realizeClass(remapClass(cls->ISA()));
#if SUPPORT_NONPOINTER_ISA
// Disable non-pointer isa for some classes and/or platforms.
// Set instancesRequireRawIsa.
// 处理non-pointer ,isa_t共用体:non-pointer 0 表示普通的 isa 指针,1 表示使用优化,存储额外信息
// 3.处理non-pointer
bool instancesRequireRawIsa = cls->instancesRequireRawIsa();
bool rawIsaIsInherited = false;
static bool hackedDispatch = false;
if (DisableNonpointerIsa) {
// Non-pointer isa disabled by environment or app SDK version
instancesRequireRawIsa = true;
}
else if (!hackedDispatch && !(ro->flags & RO_META) &&
0 == strcmp(ro->name, "OS_object"))
{
hackedDispatch = true;
instancesRequireRawIsa = true;
}
else if (supercls && supercls->superclass &&
supercls->instancesRequireRawIsa())
{
instancesRequireRawIsa = true;
rawIsaIsInherited = true;
}
if (instancesRequireRawIsa) {
cls->setInstancesRequireRawIsa(rawIsaIsInherited);
}
// SUPPORT_NONPOINTER_ISA
#endif
// Update superclass and metaclass in case of remapping
cls->superclass = supercls;
cls->initClassIsa(metacls);
// 4. 重对齐实例变量偏移地址
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();
}
}
// Connect this class to its superclass's subclass lists
// 5. 设置类之间的树形关系,方便之后遍历
if (supercls) {
addSubclass(supercls, cls);
} else {
addRootClass(cls);
}
// Attach categories
// 5. 将ro里面的method,protocol,property复制一份到rw里面
methodizeClass(cls);
return cls;
}
methodizeClass()方法
-
将ro中的method_list_t,property_list_t,protocol_list_t拷贝到rw中。
-
元类对象添加initialize类方法
-
attachCategories
static void methodizeClass(Class cls)
{
auto rw = cls->data();
auto ro = rw->ro;
method_list_t *list = ro->baseMethods();
if (list) {
// 缓存 selector,对 method list 的 selector 进行内存地址唯一化,按照 selector 的地址进行排序.
prepareMethodLists(cls, &list, 1, YES, isBundleClass(cls));
rw->methods.attachLists(&list, 1);
}
property_list_t *proplist = ro->baseProperties;
if (proplist) {
rw->properties.attachLists(&proplist, 1);
}
protocol_list_t *protolist = ro->baseProtocols;
if (protolist) {
rw->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, SEL_initialize, (IMP)&objc_noop_imp, "", NO);
}
// Attach categories.
// note: 添加 category 到类上
category_list *cats = unattachedCategoriesForClass(cls, true /*realizing*/);
attachCategories(cls, cats, false /*don't flush caches*/);
if (cats) free(cats);
}
attachCategories()方法
把 category 信息添加到类的 class_rw_t 上。
static void
attachCategories(Class cls, category_list *cats, bool flush_caches)
{
if (!cats) return;
if (PrintReplacedMethods) printReplacements(cls, cats);
//判断是否是元类对象
bool isMeta = cls->isMetaClass();
// fixme rearrange to remove these intermediate allocations
//二维数组
method_list_t **mlists = (method_list_t **)
malloc(cats->count * sizeof(*mlists));
property_list_t **proplists = (property_list_t **)
malloc(cats->count * sizeof(*proplists));
protocol_list_t **protolists = (protocol_list_t **)
malloc(cats->count * sizeof(*protolists));
int mcount = 0;
int propcount = 0;
int protocount = 0;
int i = cats->count;
bool fromBundle = NO;
//注意是逆序添加
while (i--) {
// note: 逆序添加,因此在 cats 中后面的 method 会在前面
auto& entry = cats->list[i];
//处理类对象,元类对象方法列表
method_list_t *mlist = entry.cat->methodsForMeta(isMeta);
if (mlist) {
mlists[mcount++] = mlist;
fromBundle |= entry.hi->isBundle();
}
property_list_t *proplist =
entry.cat->propertiesForMeta(isMeta, entry.hi);
if (proplist) {
proplists[propcount++] = proplist;
}
protocol_list_t *protolist = entry.cat->protocols;
if (protolist) {
protolists[protocount++] = protolist;
}
}
//这里操作的是rw
auto rw = cls->data();
//对 method list 的 selector 进行内存地址唯一化,按照 selector 的地址进行排序
prepareMethodLists(cls, mlists, mcount, NO, fromBundle);
rw->methods.attachLists(mlists, mcount);
free(mlists);
if (flush_caches && mcount > 0) flushCaches(cls);
rw->properties.attachLists(proplists, propcount);
free(proplists);
rw->protocols.attachLists(protolists, protocount);
free(protolists);
}
prepareMethodLists方法
对 method list 的 selector 进行内存地址唯一化,按照 selector 的地址进行排序。
static void
prepareMethodLists(Class cls, method_list_t **addedLists, int addedCount,
bool baseMethods, bool methodsFromBundle)
{
for (int i = 0; i < addedCount; i++) {
method_list_t *mlist = addedLists[i];
if (!mlist->isFixedUp()) {
//根据 method_t.name < method_t.name排序
fixupMethodList(mlist, methodsFromBundle, true/*sort*/);
}
}
}
attachLists()方法
将已有的数据移动到后面,新增的addList放到最前面
void attachLists(List* const * addedLists, uint32_t addedCount) {
if (addedCount == 0) return;
if (hasArray()) {
uint32_t oldCount = array()->count;
uint32_t newCount = oldCount + addedCount;
//重新分配内存
setArray((array_t *)realloc(array(), array_t::byteSize(newCount)));
//修改count
array()->count = newCount;
//先将之前的数数据往后移动
memmove(array()->lists + addedCount, array()->lists,
oldCount * sizeof(array()->lists[0]));
// 将addedLists放到array()最前面的addcount连续的位置
// C库函数 void *memcpy()从存储区 str2 复制 n 个字节到存储区 str1。
memcpy(array()->lists, addedLists,
addedCount * sizeof(array()->lists[0]));
} else if (!list && addedCount == 1) {
// 0 lists -> 1 list
list = addedLists[0];
}
else {
......
}
}
load_images方法
关键方法调用顺序
load_images()
└── prepare_load_methods()
└──call_load_methods()
load_images()方法
- 判断image中的class,category中是否含有load方法
- 将当前的image中含有load方法的class存储到add_class_to_loadable_list数组中,数组中父类在前,子类在后。Category存储到loadable_categories数组中,顺序由编译顺序确定。
- 执行add_class_to_loadable_list,loadable_categories中的load方法。
void
load_images(const char *path __unused, const struct mach_header *mh)
{
//处理load方法
//判断当前image中的class,category是否含有load方法
if (!hasLoadMethods((const headerType *)mh)) return;
//1.调用prepare_load_methods方法,
// 将父类,当前class的load具体实现存储到add_class_to_loadable_list数组中,
// 将categories中的load具体实现放到loadable_categories数组中.
// 2. 执行load方法
recursive_mutex_locker_t lock(loadMethodLock);
// Discover load methods
{
rwlock_writer_t lock2(runtimeLock);
//add_class_to_loadable_list,loadable_categories
prepare_load_methods((const headerType *)mh);
}
// Call +load methods (without runtimeLock - re-entrant)
//执行add_class_to_loadable_list,loadable_categories中的load方法
call_load_methods();
}
prepare_load_methods()方法
- 调用schedule_class_load方法,将当前的image中含有load方法的class存储到add_class_to_loadable_list数组中,数组中父类在前,子类在后。
- 将Category存储到loadable_categories数组中,顺序由编译顺序确定。
void prepare_load_methods(const headerType *mhdr)
{
size_t count, i;
runtimeLock.assertWriting();
// 1.将当前的image中含有load方法的class存储到add_class_to_loadable_list数组中,数组中父类在前,子类在后。
// 获取包含load方法的类列表,
// note: 包含 +load 方法的类 "__objc_nlclslist")
classref_t *classlist =
_getObjc2NonlazyClassList(mhdr, &count);
for (i = 0; i < count; i++) {
//父类在前,子类在后
schedule_class_load(remapClass(classlist[i]));
}
// 2.将Category存储到loadable_categories数组中,顺序由编译顺序确定。
// 包含 +load 方法的 category 列表
category_t **categorylist = _getObjc2NonlazyCategoryList(mhdr, &count);
for (i = 0; i < count; i++) {
category_t *cat = categorylist[i];
Class cls = remapClass(cat->cls);
if (!cls) continue; // category for ignored weak-linked class
realizeClass(cls);
// note: +load 之前 realize
assert(cls->ISA()->isRealized());
//编译顺序决定
add_category_to_loadable_list(cat);
}
}
schedule_class_load方法
递归调用,先父类,在子类。
static void schedule_class_load(Class cls)
{
if (!cls) return;
assert(cls->isRealized()); // _read_images should realize
if (cls->data()->flags & RW_LOADED) return;
// Ensure superclass-first ordering
// note: 先调用父类的 +load
schedule_class_load(cls->superclass);
//添加到一维数组中,[父类在前]
add_class_to_loadable_list(cls);
//标记为已经load方法已经读取过
cls->setInfo(RW_LOADED);
}
void add_class_to_loadable_list(Class cls)
{
IMP method;
method = cls->getLoadMethod();
if (!method) return;
//父类在前,子类在后
loadable_classes[loadable_classes_used].cls = cls;
loadable_classes[loadable_classes_used].method = method;
loadable_classes_used++;
}
add_category_to_loadable_list()方法
void add_category_to_loadable_list(Category cat)
{
IMP method;
method = _category_getLoadMethod(cat);
if (!method) return;
//顺序由编译先后决定
loadable_categories[loadable_categories_used].cat = cat;
loadable_categories[loadable_categories_used].method = method;
loadable_categories_used++;
}
call_load_methods()方法
- 先调用类定义中的 +load
- 再调用 category 中的 +load
void call_load_methods(void)
{
do {
while (loadable_classes_used > 0)
{
// note: 先调用类定义中的 +load
// loadable_classes[0...i]
// 先调用父类的load,在调用子类的load方法
call_class_loads();
}
// note: 再调用 category 中的 +load
more_categories = call_category_loads();
} while (loadable_classes_used > 0 || more_categories);
}
call_class_loads()方法
调用loadable_classes数组中的class中的load方法
static void call_class_loads(void)
{
int i;
struct loadable_class *classes = loadable_classes;
for (i = 0; i < used; i++) {
Class cls = classes[i].cls;
load_method_t load_method = (load_method_t)classes[i].method;
if (!cls) continue;
//方法调用
(*load_method)(cls, SEL_load);
}
call_category_loads()方法
static bool call_category_loads(void)
{
int i, shift;
bool new_categories_added = NO;
// Detach current loadable list.
struct loadable_category *cats = loadable_categories;
for (i = 0; i < used; i++) {
Category cat = cats[i].cat;
load_method_t load_method = (load_method_t)cats[i].method;
Class cls;
if (!cat) continue;
cls = _category_getClass(cat);
if (cls && cls->isLoadable()) {
//调用方法
(*load_method)(cls, SEL_load);
cats[i].cat = nil;
}
}
return new_categories_added;
}
initialize 方法
IMP lookUpImpOrForward(Class cls, SEL sel, id inst,
bool initialize, bool cache, bool resolver)
{
//// note: 首次调用方法,执行 +initialize
if (initialize && !cls->isInitialized())
{
_class_initialize (_class_getNonMetaClass(cls, inst));
}
核心方法_class_initialize()
- 先调用父类的initialize方法
- 在调用子类的initialize方法
- 调用callInitialize方法
void _class_initialize(Class cls)
{
Class supercls;
bool reallyInitialize = NO;
supercls = cls->superclass;
if (supercls && !supercls->isInitialized()) {
// note: 先 initialize 父类
_class_initialize(supercls);
}
//调用当前class的initialize方法
callInitialize(cls);
}
callInitialize方法
注意这里是objc_msgSend消息机制,先从当前类缓存中查找,再从方法列表中查找,不存在将通过super在父类中查找,依次查找。
void callInitialize(Class cls)
{
((void(*)(Class, SEL))objc_msgSend)(cls, SEL_initialize);
}
initialize 与 load区别
调用方式
- load方法:在
call_class_loads和call_category_loads中,直接拿到imp,进行方法调用 - initialize方法:通过objc_msgSend进行方法查找imp,会存在Category,父类的方法执行的情况。
调用时刻
- load方法:mian函数执行之前,也可以说是load_images中。
- initialize方法:cls第一次使用的时候。
调用顺序
- load方法:先调用父类,在调用子类,最后调用Category中的load方法,不存在被覆盖的情况。
- initialize方法:先调用父类,在调用子类,通过objc_msgSend进行方法查找,子类不存在,则调用父类的方法,会存在category同名方法覆盖的情况。
解决问题
Category结构
struct category_t {
// 名称
const char *name;
// isa
classref_t cls;
//对象方法
struct method_list_t *instanceMethods;
//类方法
struct method_list_t *classMethods;
//协议列表
struct protocol_list_t *protocols;
//实例属性列表
struct property_list_t *instanceProperties;
//class属性列表
struct property_list_t *_classProperties;
// note: 元类中保存类方法
method_list_t *methodsForMeta(bool isMeta) {
if (isMeta) return classMethods;
else return instanceMethods;
}
// 区分类对象,元类对象
propertiesForMeta(bool isMeta, struct header_info *hi){
if (!isMeta) return instanceProperties;
else if (hi->info()->hasCategoryClassProperties()) return _classProperties;
else return nil;
}
};
Category中的同名方法覆盖问题
首先我们需要知道如何通过sel找到对应的imp。
- 1.首先从当前class的缓存中查找。
- 2.从当前class的方法列表中查找。
- 3.从当前class的的父类查找,递归调用getMethodNoSuper_nolock从父类中重复1、2、3步。
从当前class的方法列表中查找
getMethodNoSuper_nolock()
└── cls->data()->methods()
└──search_method_list()
getMethodNoSuper_nolock()方法
- 循环遍历methods,methods是一个二维数组。
- 执行search_method_list方法,在一维数组中查找对应的method。一维数组查找又分二分查找和普通查找
static method_t *
getMethodNoSuper_nolock(Class cls, SEL sel)
{
//循环遍历methods,methods是一个二维数组
for (auto mlists = cls->data()->methods.beginLists(),
end = cls->data()->methods.endLists();
mlists != end;
++mlists)
{
//一维数组查找
method_t *m = search_method_list(*mlists, sel);
if (m) return m;
}
return nil;
}
search_method_list() 方法
- 二分查找
- 普通查找
static method_t *search_method_list(const method_list_t *mlist, SEL sel)
{
// fastpath:表示条件值为1的可能性很大,即很有可能执行if后面的 renturn语句,
// slowpath:则是很有可能执行else里面的语句
int methodListIsFixedUp = mlist->isFixedUp();
int methodListHasExpectedSize = mlist->entsize() == sizeof(method_t);
if (__builtin_expect(methodListIsFixedUp && methodListHasExpectedSize, 1)) {
// note: 在排序后的 method list 中进行二分查找
return findMethodInSortedMethodList(sel, mlist);
} else {
// note: 在未排序的 method list 中线性查找
for (auto& meth : *mlist) {
if (meth.name == sel) return &meth;
}
}
return nil;
}
从上面了的过程我们知道了
- 我们需要从二维数组中
cls->data()->methods查找目标imp方法。 - 循环遍历二维数组
cls->data()->methods,在每个 一维数组method_t *m中查找
我们的一维数组method_t *m数据是从attachCategories和attachLists操作中得到的。 我们可以得到这样的结论:
- Category里面同名方法会覆盖class内的【注意区分对象方法和类方法】
- 多个Category存在同名方法的,与编译顺序相关,后编译的先执行。【注意是逆序】
Category是如何合并到Class上的
这个问题我觉得可以从_read_images()、realizeClass()、attachCategories()这个三个方法的内部实现去解释。attachCategories()中的逆序是关键。需要记得要先进行realizeClass()才能执行attachCategories()操作。
Category存放在哪里,对应mach-o文件的那个段
这个问题自己能力有限,解释不清楚,后续会补充完整。
利用clang生成的代码如下。
static struct _category_t _OBJC_$_CATEGORY_Person_$_Test1 __attribute__ ((used, section ("__DATA,__objc_const"))) =
{
"Person",
0, // &OBJC_CLASS_$_Person,
(const struct _method_list_t *)&_OBJC_$_CATEGORY_INSTANCE_METHODS_Person_$_Test1,
(const struct _method_list_t *)&_OBJC_$_CATEGORY_CLASS_METHODS_Person_$_Test1,
0,
0,
};
为什么Category不能存储成员变量
我们从两个方面去回答:
category_t内部结构不存在ivars容器去存放。- ivars在编译期间就已经确定,在执行完
realizeClass()后,ro的实例变量内存偏移对齐已经完成,且ro的权限为已读。
Category与Extension的区别,如何去验证
假如我们有这样的代码
Class
@interface Person : NSObject
@property (nonatomic,assign) NSInteger age;
@end
Extension
@interface Person ()
@property (nonatomic,assign) NSInteger height;
@end
Category
@interface Person (Test1)
@property (nonatomic,assign) NSInteger weight;
@end
执行下面的代码
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc Person.m -o Person11-arm64.cpp
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc Person+Test1.m -o Person11+Test1-arm64.cpp
ivars列表中包含了Extension中的height。说明Extension是Class的一部分。【可以编写多个方法,属性去验证】
static struct /*_ivar_list_t*/ {
unsigned int entsize;
unsigned int count;
struct _ivar_t ivar_list[2];
} _OBJC_$_INSTANCE_VARIABLES_Person __attribute__ ((used, section ("__DATA,__objc_const"))) = {
sizeof(_ivar_t),
2,
{{(unsigned long int *)&OBJC_IVAR_$_Person$_age, "_age", "q", 3, 8},
{(unsigned long int *)&OBJC_IVAR_$_Person$_height, "_height", "q", 3, 8}}
};
Category会生成一个_category_t结构体,这个结构体会在运行期间执行attachCategories操作将属性,协议,方法附加到class上。
static struct _category_t _OBJC_$_CATEGORY_Person_$_Test1 __attribute__ ((used, section ("__DATA,__objc_const"))) =
{
"Person",
0, // &OBJC_CLASS_$_Person,
(const struct _method_list_t *)&_OBJC_$_CATEGORY_INSTANCE_METHODS_Person_$_Test1,
(const struct _method_list_t *)&_OBJC_$_CATEGORY_CLASS_METHODS_Person_$_Test1,
0,
(const struct _prop_list_t *)&_OBJC_$_PROP_LIST_Person_$_Test1,
};
Category的作用
- 覆写class内的方法,比如:我们要修改某个SDK的方法
- 裁分class的业务逻辑,将业务分为多个category,已达到将业务下沉的目的。
- 为class添加私有属性,隐藏具体的实现。
为什么要设计Load这个方法
- load方法都会执行,不存在被覆盖的情况。
- 其他我不知道的原因
Load方法耗时
待完善