接上篇结尾,提到了类的实现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赋值,如:
attachCategoriesdemangledNameclass_setVersionaddMethods_finishclass_addProtocol_class_addPropertyobjc_duplicateClass所以rwe必然有值。
采用反推法
全局搜索attachCategories调用的位置:
attachToClassload_categories_nolock全局搜索attachToClass调用的位置:methodizeClass
当
previously为true的时候,才能调用判断条件里面的attachToClass方法。
previously是methodizeClass的参数
static void methodizeClass(Class cls, Class previously)
全局搜索methodizeClass调用的位置:
realizeClassWithoutSwiftpreviously也是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()里