Swift调试结构体数组时遇不同实例元素内存地址重复的问题咨询
Swift 两次初始化打印相同数组元素内存地址的原因解析
现象本质
你观察到两次打印完全一致的内存地址,是Swift值类型内存机制下的正常表现,不属于内存异常或者对象共享问题,核心原因来自两个层面:
- 你打印的地址并非实例属性的持久化地址
你在init方法里通过address(o: &cars[0])获取的地址,指向的是方法内部临时创建的局部变量cars的栈空间。当init方法执行完毕后,这个局部变量的生命周期立即结束,它占据的栈内存会被标记为可复用的空闲空间。
由于两次调用Garage()的执行逻辑完全一致,函数调用的栈帧偏移完全相同,第二次执行init时,新创建的局部cars变量会直接复用第一次初始化时已经释放的那块栈内存,因此两次打印的地址值完全一样。 - 空结构体属于零大小类型,进一步强化了地址复用的表现
你定义的Car是没有任何存储属性的空结构体,在Swift中属于零大小类型(ZST),本身不占用任何实际内存空间。你通过&cars[0]拿到的地址,本质是数组容器的缓冲区起始地址,并非Car实例本身的独立存储地址——对零大小类型来说,哪怕是不同的实例,取到的缓冲区地址也不具备实际的内存指向语义。
常见认知偏差
很多开发者会默认数组元素的地址是和数组实例绑定的持久化地址,但实际上在值类型的传递过程中,数组的存储会从栈上的临时缓冲区,在赋值时转换为堆上的持久化共享缓冲区。你打印地址的时机刚好在这个转换完成之前,自然拿不到最终的持久化存储地址。
另外栈内存的复用本身是系统的正常优化行为:函数退出时栈指针回退的过程不会主动清空内存内容,同一段代码的两次连续调用,往往会复用同一块空闲栈空间,这和堆内存每次分配新独立地址的行为存在本质区别。
验证方式
如果想要直观确认两个实例的独立性,可以把打印逻辑从init方法内部移到实例创建完成之后,直接访问实例持有的cars属性取地址:
let firstGarage = Garage() let secondGarage = Garage() // 打印实例持久化存储对应的数组缓冲区地址 print(NSString(format: "%p", address(o: &firstGarage.cars[0]))) print(NSString(format: "%p", address(o: &secondGarage.cars[0])))
此时输出的两个地址会存在明显差异,也能直接证明两个Garage实例的存储完全独立,不存在内存重叠或者共享的问题。
内容的提问来源于stack exchange,提问作者Balázs Vincze
相关产品推荐
相关产品推荐

