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

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

1.调试代码准备

下载objc818可调试源码git

2.realizeClassWithoutSwift分析

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

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添加到方法列表markdown

image.png 按照地址进行排序函数

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

image.png

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

image.png 仍是没有数据,因此目前方法还没加到类里面去。测试

2.1.2 分类的引入

methodizeClass方法里面ui

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()
相关文章
相关标签/搜索