为何在Objective-C中&object会得出两个不同的内存地址值?
咱们来一步步拆解这个问题,你看到的地址差异其实是ARC(自动引用计数)在背后悄悄做了手脚,再结合x86_64架构下栈内存的布局特点导致的。
先搞清楚两个打印分别输出的是什么
首先看你的main函数代码:
NSObject* object; printf("value of NSObject** in main: %p\n", &object);
这里的&object取的是main函数内部局部变量object(一个NSObject类型的指针)的内存地址,这个地址位于栈内存上,也就是输出里的0x7ffee1217668。
你调用function(&object)时,本意是把这个地址传递给function的参数,但function里的打印逻辑:
printf("value of NSObject** in function: %p\n", object);
这里的object是function自己的局部参数(类型是NSObject**)——正常情况下,它应该直接保存你传进来的&object的值,两个打印结果应该完全一致,但ARC打破了这个预期。
ARC为什么要干预参数传递?
在ARC模式下,编译器会自动管理Objective-C对象的生命周期。当它看到你传递一个NSObject**类型的参数时,会默认你可能要通过这个指针修改指向的对象(比如在函数内部创建新对象并赋值给它)。为了保证内存安全,ARC会悄悄做这两件事:
- 自动将main里未初始化的
object初始化为nil(ARC的规则:未赋值的Objective-C对象指针会被自动置空); - 创建一个临时的NSObject*变量,把main里
object的nil值拷贝进去,然后把这个临时变量的地址传递给function的参数object。
为什么地址刚好差8字节?
在x86_64架构下,栈内存是向下增长的——也就是新分配的变量地址会比之前的变量地址更小。而且每个指针类型的变量在x86_64下占8字节,main里的object和ARC创建的临时变量是栈上相邻的两个局部变量,所以它们的地址正好相差8字节,这就是你看到两个输出值差8的原因。
如何验证这个结论?
如果你用-fno-objc-arc(关闭ARC)重新编译这段代码,ARC不再干预参数传递,那么function里打印的object值就会和main里打印的&object完全一致——因为这时候传递的就是main里object的真实地址,没有临时变量插进来捣乱。
内容的提问来源于stack exchange,提问作者Cornstalks

