Swift中existential containers是否用Copy on Write?带protocol type参数的函数调用会堆分配吗?
Swift 中 protocol type 作为函数参数的堆分配行为说明
核心结论:调用参数为 protocol type(存在类型,existential type)的函数是否触发堆分配,取决于三个条件:实例的内存大小、实例是否实现 Copy on Write(COW)机制、编译优化等级。
具体规则
- 内存小于等于 existential 内联缓冲区的实例不会触发堆分配
Swift 中默认的 existential container 固定占用3个机器字(64位系统下为24字节),包含1个3字长的内联值缓冲区、1个类型元数据指针、1个协议目击表指针。如果传入的值类型实例内存大小不超过3个机器字,会直接存储在内联缓冲区中,全程不会产生堆分配。 - 超出内联缓冲区的非COW实例会触发堆分配
如果实例内存超过内联缓冲区大小,且该类型没有实现COW机制,那么将实例赋值给 protocol type 变量、或者作为 protocol type 参数传递给函数时,会将完整实例拷贝到堆内存,existential container 仅存储堆地址,此时会产生一次堆分配。 - COW类型实例传递给非inout参数不会产生新堆分配
如果实例类型本身实现了COW机制,你将它作为非inout的 protocol type 参数传递时,仅会对底层堆实例的引用计数+1,不会触发新的堆分配。只有当你通过inout参数修改实例、或者对参数做可变操作时,才会触发写时复制,产生新的堆分配。 - 泛型约束参数完全避免堆分配
如果你将函数参数替换为带协议约束的泛型参数(即写法为func test<T: MyProtocol>(param: T),而非func test(param: MyProtocol)),编译器会为每个传入的具体类型生成特化的函数版本,不需要借助 existential container 做类型擦除,无论实例大小都不会产生堆分配,是性能敏感场景的首选方案。
额外优化提示
开启 Release 模式编译优化(-O 参数)后,编译器会自动执行装箱消除(Box Elimination)优化,大量场景下可以直接消除 protocol type 参数的堆分配开销,不需要手动修改代码。
内容的提问来源于stack exchange,提问作者Yuji
相关产品推荐
相关产品推荐

