iOS底层探索类的加载(二)

704 阅读4分钟

上篇结尾,提到了类的实现realizeClassWithoutSwift,那么realizeClassWithoutSwift内部干了些什么?

1.调试代码准备

下载objc818可调试源码

2.realizeClassWithoutSwift分析

也在该函数内也添加if判断代码,只查看该函数处理LGPerson的实现,方便调试

image.png

image.png image.png

image.png

image.png

image.png

image.png 猜想方法列表应该在methodizeClass处理

2.1.methodizeClass分析

image.png

2.1.1.prepareMethodLists分析

image.png

fixupMethodList分析

image.pngsel添加到方法列表

image.png 按照地址进行排序

image.png 添加未排序前打印和排序后打印

image.png

经过prepareMethodLists一系列处理后,查看ro方法列表

image.png 还是没有数据,所以目前方法还没加到类里面去。

2.1.2 分类的引入

methodizeClass方法里面

image.png

rwe是什么?可参看这篇文章,怎么拿到rwe

image.png

image.png

image.png

全局搜索extAllocIfNeeded,有多处使用extAllocIfNeededrwe赋值,如:

  • attachCategories
  • demangledName
  • class_setVersion
  • addMethods_finish
  • class_addProtocol
  • _class_addProperty
  • objc_duplicateClass 所以rwe必然有值。

采用反推法 全局搜索attachCategories调用的位置:

  • attachToClass
  • load_categories_nolock 全局搜索attachToClass调用的位置:
  • methodizeClass

image.pngpreviouslytrue的时候,才能调用判断条件里面的attachToClass方法。 previouslymethodizeClass的参数

static void methodizeClass(Class cls, Class previously)

全局搜索methodizeClass调用的位置:

  • realizeClassWithoutSwift previously也是realizeClassWithoutSwift的参数 全局搜索realizeClassWithoutSwift调用的位置发现 previouslynil作为参数,所以上图中的方法不会调用。那么久来到了:

image.png

全局搜索load_categories_nolock

  • loadAllCategories
  • _read_images

image.png image.png 了解了attachCategories被调起的流程,那么来看看attachCategories内部怎么实现的

2.1.3.attachCategories分析

image.png

2.1.4.attachLists 算法

image.png

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

realizeClassWithoutSwiftimage.png

image.png

image.png

image.png image.png mlist地址存到mlists中了

image.png image.png

3.5.2.有多个分类

补充测试代码:

@interface LGPerson (LGB)
- (void)cateFuncB;
@end

@implementation LGPerson (LGB)
//更具情况添加或删除
+ (void)load{}
- (void)cateFuncB{
    NSLog(@"%s",__func__);
}
@end

流程跟踪:

image.png

image.png image.png

3.5.3.分类实现load,主类没有实现load

image.png dyld的时候已经合到一起了,存在data()里面

3.5.4.主类实现load,分类没有实现load

image.png dyld的时候已经合到一起了,存在data()里面

3.5.5.都没有实现

image.png image.png image.png

都没有实现会推迟到第一次消息发送的时候进行初始化,它们的方法都已经加载到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

image.png

image.png _category_t分类结构体结构

image.png 方法列表里面却却没有getset方法,所以分类添加属性需要自己实现getset方法关联对象

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()