前言
属性
通过 clang
编译的 cpp
文件中可以发现 copy & strong & weak
修饰, 在编译的底层代码中是有区别的
【补充知识】:Type Encoding & Property Type String
【Type encoding】:编码类型
获取 Type encoding
有两种方式:
【方式一】:通过 代码
获取
在 main.m
中添加添加函数方法:
1 2 3 4 5
| #ifdef DEBUG #define ZJLog(format, ...) printf("%s\n", [[NSString stringWithFormat:format, ## __VA_ARGS__] UTF8String]); #else #define ZJLog(format, ...); #endif
|
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
中声明两个属性 nickName
和 name
,分别用 copy
和 strong
修饰
1 2 3 4
| @interface ZJPerson : NSObject @property (nonatomic, copy) NSString *nickName; @property (nonatomic, strong) NSString *name; @end
|
- 通过
clang
将 main.m
文件编译成 main.cpp
,然后发现 copy
和 strong
修饰的属性的 set方法
是有区别的,如下图:
这里就有了疑问,为什么 copy
修饰的 nickName属性
使用了 objc_setProperty
,而 strong
的没有?
想要分析 copy
和 strong
在底层是如何实现的,需要分析 LLVM源码
下载好 LLVM源码
后,在 LLVM
中搜索 objc_setProperty
,找到如下所示的 getOptimizedSetPropertyFn
方法中:
由上图可以看出:
如果是 atomic & copy
修饰,name
为 objc_setProperty_atomic_copy
如果是 atomic & !copy
修饰,name
为 objc_setProperty_atomic
如果是 nonatomic & copy
修饰,name
为 objc_setProperty_nonatomic_copy
其他剩余的组合,即 nonatomic、nonatomic & strong、nonatomic & weak
等,name
为 objc_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
方法有调用,然后在这里发现了 strong
和 weak
的不同处理