接上篇结尾,提到了类的实现realizeClassWithoutSwift
,那么realizeClassWithoutSwift
内部干了些什么?
1.调试代码准备
2.realizeClassWithoutSwift分析
也在该函数内也添加if
判断代码,只查看该函数处理LGPerson
的实现,方便调试
猜想方法列表应该在methodizeClass
处理
2.1.methodizeClass分析
2.1.1.prepareMethodLists分析
fixupMethodList分析
将sel
添加到方法列表
按照地址进行排序
添加未排序前打印和排序后打印
经过prepareMethodLists
一系列处理后,查看ro
方法列表
还是没有数据,所以目前方法还没加到类里面去。
2.1.2 分类的引入
methodizeClass
方法里面
rwe
是什么?可参看这篇文章,怎么拿到rwe
?
全局搜索extAllocIfNeeded
,有多处使用extAllocIfNeeded
给rwe
赋值,如:
attachCategories
demangledName
class_setVersion
addMethods_finish
class_addProtocol
_class_addProperty
objc_duplicateClass
所以rwe
必然有值。
采用反推法
全局搜索attachCategories
调用的位置:
attachToClass
load_categories_nolock
全局搜索attachToClass
调用的位置:methodizeClass
当previously
为true
的时候,才能调用判断条件里面的attachToClass
方法。
previously
是methodizeClass
的参数
static void methodizeClass(Class cls, Class previously)
全局搜索methodizeClass
调用的位置:
realizeClassWithoutSwift
previously
也是realizeClassWithoutSwift
的参数 全局搜索realizeClassWithoutSwift
调用的位置发现previously
为nil
作为参数,所以上图中的方法不会调用。那么久来到了:
全局搜索load_categories_nolock
loadAllCategories
_read_images
了解了attachCategories
被调起的流程,那么来看看attachCategories
内部怎么实现的
2.1.3.attachCategories分析
2.1.4.attachLists 算法
3.分类和类搭配加载
分类和类搭配加载有以下四种情况。现在对这四种情况分别分析。
目标是在加载主类后,查看ro
里面有没有分类数据,如果有分类数据,就说明分类加载有可能在编译之前就完成。
3.1.分类和类都有load方法
调用顺序: _read_images
非懒加载类> realizeClassWithoutSwift
>methodizeClass
>attachToClass
>load_categories_nolock
>attachCategories
>
3.2.分类有load方法,主类没有
调用顺序:_read_images
非懒加载类 > realizeClassWithoutSwift
> methodizeClass
> attachToClass
注意:没有调用
attachCategories
3.3.主类有load方法,分类没有
调用顺序:_read_images
非懒加载类 > realizeClassWithoutSwift
> methodizeClass
> attachToClass
注意:也没有调用
attachCategories
3.4.分类和类都没有load方法
什么都不会调用,对象第一次发送消息后才会有调用。
3.5.分类加载流程跟踪
3.5.1.只有一个分类
当前以主类和分类都有load
方法为例
测试代码:
@interface LGPerson : NSObject
@property (nonatomic, copy) NSString *name;
- (void)personFunc;
@end
@implementation LGPerson
//更具情况添加或删除
+ (void)load{}
- (void)saySomething{
NSLog(@"%s",__func__);
}
@end
@interface LGPerson (LGA)
@property (nonatomic, copy) NSString *cateA_name;
- (void)cateFunc;
@end
@implementation LGPerson (LGA)
//更具情况添加或删除
+ (void)load{}
- (void)cateFunc{
NSLog(@"%s",__func__);
}
@end
在realizeClassWithoutSwift
中
mlist
地址存到mlists
中了
3.5.2.有多个分类
补充测试代码:
@interface LGPerson (LGB)
- (void)cateFuncB;
@end
@implementation LGPerson (LGB)
//更具情况添加或删除
+ (void)load{}
- (void)cateFuncB{
NSLog(@"%s",__func__);
}
@end
流程跟踪:
3.5.3.分类实现load,主类没有实现load
在 dyld
的时候已经合到一起了,存在data()
里面
3.5.4.主类实现load,分类没有实现load
在 dyld
的时候已经合到一起了,存在data()
里面
3.5.5.都没有实现
都没有实现会推迟到第一次消息发送的时候进行初始化,它们的方法都已经加载到data
里面了。
4.分类的本质
1.在main.m
中添加分类代码
#import <Foundation/Foundation.h>
#import "LGPerson.h"
#import <objc/runtime.h>
@interface LGPerson (LG) <NSObject>
@property (nonatomic, copy) NSString *cate_name;
@property (nonatomic, assign) int cate_age;
- (void)cate_instanceMethod1;
- (void)cate_instanceMethod2;
+ (void)cate_classMethod3;
@end
@implementation LGPerson (LG)
- (void)cate_instanceMethod1{
NSLog(@"%s",__func__);
}
- (void)cate_instanceMethod2{
NSLog(@"%s",__func__);
}
+ (void)cate_classMethod3{
NSLog(@"%s",__func__);
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
LGPerson *p = [LGPerson alloc];
[p cate_instanceMethod2];
NSLog(@"Hello, World!");
}
return 0;
}
3.将main.m
编译成c++
文件
clang -rewrite-objc main.m -o main.cpp
_category_t
分类结构体结构
方法列表里面却却没有get
和set
方法,所以分类添加属性需要自己实现get
和set
方法关联对象
5.懒加载类与非懒加载类的加载
是不是懒加载类:当前类是否实现load
方法
- 非懒加载类在
map_images
的时候加载所有的数据:map_images
>map_images_nolock
>_read_images
>readClass
>_getObjc2ClassList
>realizeClassWithoutSwift
>methodizeClass
- 懒加载类数据加载推迟到第一次消息执行:
lookUpImpOrForward
>initializeAndMaybeRelock
>realizeClassMaybeSwiftMaybeRelock
>realizeClassWithoutSwift
>methodizeClass
6.总结
- 判断是不是懒加载类:当前类是否实现
load
方法 - 分类的方法和类方法一样也是从
macho
里读取的,如果其实现load
方法,就会调用attachCategories
,进行一系列的计算,所以load
方法耗时。 - 没有实现
load
方法,方法会在dyld
的时候加载并存到data()
里