OC底层原理12:copy & strong & weak底层分析

张建 lol

前言

属性 通过 clang 编译的 cpp 文件中可以发现 copy & strong & weak 修饰, 在编译的底层代码中是有区别的

【补充知识】:Type Encoding & Property Type String

【Type encoding】:编码类型

获取 Type encoding 有两种方式:

【方式一】:通过 代码 获取

main.m 中添加添加函数方法:

  • ZJLog:打印结果描述
1
2
3
4
5
#ifdef DEBUG
#define ZJLog(format, ...) printf("%s\n", [[NSString stringWithFormat:format, ## __VA_ARGS__] UTF8String]);
#else
#define ZJLog(format, ...);
#endif
  • zjTypes 函数:打印各种类型编码函数
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
#pragma mark - 各种类型编码
void zjTypes(){
NSLog(@"char --> %s",@encode(char));
NSLog(@"int --> %s",@encode(int));
NSLog(@"short --> %s",@encode(short));
NSLog(@"long --> %s",@encode(long));
NSLog(@"long long --> %s",@encode(long long));
NSLog(@"unsigned char --> %s",@encode(unsigned char));
NSLog(@"unsigned int --> %s",@encode(unsigned int));
NSLog(@"unsigned short --> %s",@encode(unsigned short));
NSLog(@"unsigned long --> %s",@encode(unsigned long long));
NSLog(@"float --> %s",@encode(float));
NSLog(@"bool --> %s",@encode(bool));
NSLog(@"void --> %s",@encode(void));
NSLog(@"char * --> %s",@encode(char *));
NSLog(@"id --> %s",@encode(id));
NSLog(@"Class --> %s",@encode(Class));
NSLog(@"SEL --> %s",@encode(SEL));
int array[] = {1,2,3};
NSLog(@"int[] --> %s",@encode(typeof(array)));
typedef struct person{
char *name;
int age;
}Person;
NSLog(@"struct --> %s",@encode(Person));

typedef union union_type{
char *name;
int a;
}Union;
NSLog(@"union --> %s",@encode(Union));

int a = 2;
int *b = {&a};
NSLog(@"int[] --> %s",@encode(typeof(b)));
}

我们在 main.m 函数中调用上面的函数

1
2
3
4
5
6
7
int main(int argc, const char * argv[]) {
@autoreleasepool {
zjTypes();
NSLog(@"Hello, World!");
}
return 0;
}

查看输出的打印结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
2020-10-17 14:45:23.857263+0800 001-类的属性与变量[24305:733568] char --> c
2020-10-17 14:45:23.857725+0800 001-类的属性与变量[24305:733568] int --> i
2020-10-17 14:45:23.857763+0800 001-类的属性与变量[24305:733568] short --> s
2020-10-17 14:45:23.857818+0800 001-类的属性与变量[24305:733568] long --> q
2020-10-17 14:45:23.857887+0800 001-类的属性与变量[24305:733568] long long --> q
2020-10-17 14:45:23.857936+0800 001-类的属性与变量[24305:733568] unsigned char --> C
2020-10-17 14:45:23.857965+0800 001-类的属性与变量[24305:733568] unsigned int --> I
2020-10-17 14:45:23.857989+0800 001-类的属性与变量[24305:733568] unsigned short --> S
2020-10-17 14:45:23.858012+0800 001-类的属性与变量[24305:733568] unsigned long --> Q
2020-10-17 14:45:23.858065+0800 001-类的属性与变量[24305:733568] float --> f
2020-10-17 14:45:23.858105+0800 001-类的属性与变量[24305:733568] bool --> B
2020-10-17 14:45:23.858143+0800 001-类的属性与变量[24305:733568] void --> v
2020-10-17 14:45:23.858172+0800 001-类的属性与变量[24305:733568] char * --> *
2020-10-17 14:45:23.858196+0800 001-类的属性与变量[24305:733568] id --> @
2020-10-17 14:45:23.858222+0800 001-类的属性与变量[24305:733568] Class --> #
2020-10-17 14:45:23.862415+0800 001-类的属性与变量[24305:733568] SEL --> :
2020-10-17 14:45:23.862459+0800 001-类的属性与变量[24305:733568] int[] --> [3i]
2020-10-17 14:45:23.862516+0800 001-类的属性与变量[24305:733568] struct --> {person=*i}
2020-10-17 14:45:23.862591+0800 001-类的属性与变量[24305:733568] union --> (union_type=*i)
2020-10-17 14:45:23.862647+0800 001-类的属性与变量[24305:733568] int[] --> ^i

【方式二】:查看官方文档

Type Encoding-官方文档

【结论】:

由上面的结果我们可知:

  • 每一个 变量类型 都对应一个 编码类型,如 char -> c 等等。

【Property Type String】:属性类型编码

属性类型编码可以通过下面的官方文档学习:

Property Type String-官方文档

我们前面学习了,属性 通过 clang 编译 cpp 后,会生成对应的 成员变量方法列表 属性列表,如下:

【方法列表】:

【属性列表】:

【成员变量列表】:

方法列表中@16@0:8 到底是什么意思?

  • @16@0:8 为例:

    • 第一个参数@:返回值
    • 第二个参数16:共用16字节
    • 第三个参数@:第一个参数
    • 第四个参数0:从0开始(0~8)
    • 第五个参数 号:sel 从 8 号位置开始
    • 第六个参数8:从8开始(8~16)

属性列表中 "name","T@\"NSString\",&,N,V_name" 是什么意思?

  • "name","T@\"NSString\",&,N,V_name" 为例:

    • T 表示 type
    • @ 表示 变量类型
    • C 表示 copy
    • N 表示 nonatomic
    • V 表示 variable 变量,即下划线变量 _nickName

copy & strong & weak 底层分析

  • ZJPerson 中声明两个属性 nickNamename ,分别用 copystrong 修饰
1
2
3
4
@interface ZJPerson : NSObject
@property (nonatomic, copy) NSString *nickName;
@property (nonatomic, strong) NSString *name;
@end
  • 通过 clangmain.m 文件编译成 main.cpp,然后发现 copystrong 修饰的属性的 set方法 是有区别的,如下图:

这里就有了疑问,为什么 copy 修饰的 nickName属性 使用了 objc_setProperty ,而 strong 的没有?

想要分析 copystrong 在底层是如何实现的,需要分析 LLVM源码

下载好 LLVM源码 后,在 LLVM 中搜索 objc_setProperty ,找到如下所示的 getOptimizedSetPropertyFn 方法中:

由上图可以看出:

  • 如果是 atomic & copy 修饰,nameobjc_setProperty_atomic_copy

  • 如果是 atomic & !copy 修饰,nameobjc_setProperty_atomic

  • 如果是 nonatomic & copy 修饰,nameobjc_setProperty_nonatomic_copy

  • 其他剩余的组合,即 nonatomic、nonatomic & strong、nonatomic & weak 等,nameobjc_setProperty_nonatomic

我们可以通过 汇编调试 查看最终到底会执行哪个命令:

我们发现 不管是 copy修饰 还是 strong修饰 还是 weak修饰 的属性,最终都会执行 objc_storeStrong

  • objc4源码 中搜索 objc_storeStrong,如下:
1
2
3
4
5
6
7
8
9
10
11
void
objc_storeStrong(id *location, id obj)
{
id prev = *location;
if (obj == prev) {
return;
}
objc_retain(obj); // retain新值
*location = obj;
objc_release(prev); // release旧值
}

主要也是 retain新值,release旧值

  • 回到 LLVM 源码中搜索 objc_storeStrong,我们发现其底层调用的是 EmitARCStoreStrongCall ,如下图:

  • LLVM 中搜索 EmitARCStoreStrongCall 方法,在GenerateCopyHelperFunction 方法有调用,然后在这里发现了 strongweak 的不同处理

  • Post title:OC底层原理12:copy & strong & weak底层分析
  • Post author:张建
  • Create time:2020-09-28 21:58:37
  • Post link:https://redefine.ohevan.com/2020/09/28/OC底层原理/OC底层原理12:copy-strong-weak底层分析/
  • Copyright Notice:All articles in this blog are licensed under BY-NC-SA unless stating additionally.
On this page
OC底层原理12:copy & strong & weak底层分析