环境说明:
从githud 上下载一份编译好的runtime源码(我使用的是objc-818版本)
首先创建一个ELPerson类,添加一个strong修饰的属性strongPerson(NSObject类型)
为了便于观察给strongPerson变量赋值时,系统调用了哪些方法,我添加了一个personConfig的方法,在该方法中为strongPerson赋值。
在main方法中创建ELPerson对象,同时打印改对象的地址,调用对象的personConfig方法
将ELPerson.m 文件使用clang命令转成.cpp查看系统实现 ( clang -rewrite-objc ELPerson.m)
运行程序,并使用LLDB指令(breakpoint set -n personConfig)将断点设置在personConfig方法里
这时可以查看到personConfig的汇编实现。
从注释可以看出来,当给strongPerson赋值时,会调用setStrongPerson方法。
继续将断点设置在setStrongPerson:方法中 ( breakpoint set -n setStrongPerson:)
此时可查看一下ELPerson.cpp中的c++实现。
搜索 setStrongPerson:
在ELPerson 的方法列表中可以查看到以下方法,
self + OBJC_IVAR_$_ELPerson$_strongPerson)) = strongPerson;
// OBJC_IVAR_$_ELPerson$_strongPerson 实现
extern "C" unsigned long int OBJC_IVAR_$_ELPerson$_strongPerson __attribute__ ((used, section ("__DATA,__objc_ivar"))) = __OFFSETOFIVAR__(struct ELPerson, _strongPerson);
#define __OFFSETOFIVAR__(TYPE, MEMBER) ((long long) &((TYPE *)0)->MEMBER)
__OFFSETOFIVAR__ 这个宏定义是取到_strongPerson 在结构体ELPerson中的偏移量,知道偏移量self + OBJC_IVAR_$_ELPerson$_strongPerson 就可以得到_strongPerson的需要存储的地址位置
复制代码
对应到oc代码,就是将strongPerson的值赋值给_strongPerson
NSObject * strongPerson = [[NSObject alloc]init];
self.strongPerson = strongPerson;
复制代码
对应到汇编代码,
0x100003e20 <+0>: pushq %rbp
0x100003e21 <+1>: movq %rsp, %rbp
0x100003e24 <+4>: subq $0x20, %rsp
0x100003e28 <+8>: movq %rdi, -0x10(%rbp)
0x100003e2c <+12>: movq %rsi, -0x18(%rbp)
0x100003e30 <+16>: movq %rdx, -0x8(%rbp)
0x100003e34 <+20>: movq -0x8(%rbp), %rsi
0x100003e38 <+24>: movq -0x10(%rbp), %rdi
0x100003e3c <+28>: addq $0x8, %rdi
0x100003e43 <+35>: callq 0x100003e96 ; symbol stub for: objc_storeStrong
0x100003e48 <+40>: addq $0x20, %rsp
-> 0x100003e4c <+44>: popq %rbp
复制代码
通过指令读取寄存器中的值:
rdi 中存储的是 ELPerson 对象的地址
rsi 中存储的是 strongPerson 对象的地址
addq _ELPerson$_strongPerson
找到变量 _strongPerson 在结构体中需要存储的位置,调用 objc_storeStrong 方法,并进行赋值。
断点到 objc_storeStrong 方法中:
location 是self+偏移量的地址 (self 是ELPerson 对象类型)
obj 是创建出来的strongPerson对象
objc_retain(obj); 先对strongPerson做了retain操作,
*location = obj; 然后将strongPerson的值 赋值到 location 这块地址中
objc_release(prev); 释放location地址中旧的值
验证一下:
至此setStrongPerson 方法调用完毕。
同理,创建weak属性的weakPerson属性,并在personConfig为其赋值。
将断点设置在setWeakPerson:方法中 (breakpoint set -n setWeakPerson: )
可以看到setWeakPerson方法中调用的是objc_storeWeak 方法进行赋值操作
将断点设置在objc_storeWeak方法中:
location 是self+偏移量的地址 (self 是ELPerson 对象类型)
obj 是创建出来的weakPerson对象
继续调用storeWeak方法
概括来讲同样是释放旧值,赋值新值到location地址中的操作。
与strong不同的是,不会增加对象的强引用,而是加入到弱引用表中。