酷酷的哀殿


  • 首页

  • 技术

  • 笔记

  • 杂记

  • Todo

  • 关于

  • 搜索
close

iOS runtime 研究笔记

时间: 2021-04-07   |   阅读: 994 字 ~2分钟

前言

因为某个神秘的原因,对多个动态库的加载机制做了一些简单的测试。

ps. 部分测试结果可能会颠覆很多网友的认知

一、分类的 +load 方法执行时机可能早于类自身的 +load 方法

首先,我们分别准备两个动态库并添加下面的测试代码

动态库 A:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
#import <FrameWorkB/ClassB.h>
  
@implementation ClassB (my)

+ (void)load {
    NSLog(@"开始执行 ClassB 的 分类 方法");
}

@end

@implementation ClassA

+ (void)load {
    NSLog(@"开始执行 ClassA 的 load 方法");
}

@end

动态库 B:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
#import <FrameWorkA/ClassA.h>

@implementation ClassA (my)

+ (void)load {
    NSLog(@"开始执行 ClassA 的 分类 方法");
}

@end

@implementation ClassB

+ (void)load {
    NSLog(@"开始执行 ClassB 的 load 方法");
}

@end

对应日志如下:

注意:ClassA 分类 的 +load 方法执行时机早于 ClassA 自身的 +load 方法

原因分析:

  1. 启动时,dyld 会依次读取 APP 依赖的动态库
  2. 逆序通知 libobjc.A.dylib 的 load_images 函数执行加载任务
  3. 按照以下顺序执行 load 方法:
    1. 类自身的 +load 方法
    2. 分类的 +load 方法
  4. C++ 的 静态初始化方法 和通过 __attribute__((constructor)) 修饰的函数

本例中,FrameWorkB 是最后一个被依赖的动态库,所以,FrameWorkB 的内部的 ClassA 分类 的 +load 方法执行时机会早于 ClassA 自身的 +load 方法

二、分类的实例方法会覆盖类自身的实例方法

动态库 A:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
#import <FrameWorkB/ClassB.h>

@implementation ClassA

- (void)test {
    NSLog(@"开始执行 ClassA 的 test 方法");
}

@end

@implementation ClassB (MY)

- (void)test {
    NSLog(@"开始执行 ClassB 分类 的 test 方法");
}

@end

动态库 B:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
#import <FrameWorkA/ClassA.h>

@implementation ClassA (my)

- (void)test {
    NSLog(@"开始执行 ClassA 分类 的 test 方法");
}

@end

@implementation ClassB

- (void)test {
    NSLog(@"开始执行 ClassB 的 test 方法");
}

@end

主程序:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
void dumpPath(const char *path) {
    NSString *str = [NSString stringWithUTF8String:path];
    NSLog(@"路径:%@", [str componentsSeparatedByString:@"/"].lastObject);
}

+ (void)load {

    NSLog(@"===验证 ClassA 和 ClassB 分别属于不同的 动态库===");
    NSLog(@"%@", [NSBundle bundleForClass:ClassA.class].bundleIdentifier);
    NSLog(@"%@", [NSBundle bundleForClass:ClassB.class].bundleIdentifier);

    NSLog(@"===打印 ClassA 的方法列表 ===");
    unsigned int count = 0;
    Method *m = class_copyMethodList([ClassA class], &count);
    for (int i=0; i<count; i++) {
        NSLog(@"%p", method_getImplementation(m[i]));
        
        Dl_info info;
        dladdr(method_getImplementation(m[i]), &info);
        dumpPath(info.dli_fname);
        NSLog(@"%s\n\n", info.dli_sname);
    }
    NSLog(@"===打印 ClassB 的方法列表 ===");
    m = class_copyMethodList([ClassB class], &count);
    for (int i=0; i<count; i++) {
        NSLog(@"方法地址:%p", method_getImplementation(m[i]));

        Dl_info info;
        dladdr(method_getImplementation(m[i]), &info);
        dumpPath(info.dli_fname);
        NSLog(@"%s\n\n", info.dli_sname);
    }
    NSLog(@"===验证 实例方法覆盖 ===");
    [[ClassA new] performSelector:@selector(test)];
    [[ClassB new] performSelector:@selector(test)];
}

通过日志,我们可以发现分类的 test 方法一定会覆盖类自身的 test 方法

相关推荐

  • 跟着 NSDictionary 的底层设计学习优化技巧(2)
  • 跟着 NSDictionary 的底层设计学习优化技巧(3)
  • iOS 各种奇怪的崩溃
  • 通过 php 的语法设计将判断 NSArray 数组是否包含指定元素的时间复杂度从 O(n) 降为 O(1)
  • Xcode 新构建系统 与 cmake 的兼容性解决方案
#iOS# #runtime#
iOS 各种奇怪的崩溃
分享一些有趣的经历
  • 文章目录
  • 站点概览
酷酷的哀殿

酷酷的哀殿

单身狗

74 日志
83 标签
    • 前言
    • 一、分类的 +load 方法执行时机可能早于类自身的 +load 方法
    • 二、分类的实例方法会覆盖类自身的实例方法
© 2021 酷酷的哀殿 京ICP备18052541号-2
Powered by - Hugo v0.80.0
Theme by - NexT
0%