You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Swift中存储含String的结构体为字节数组,如何避免其被释放?

在Swift中将含String的结构体存储为字节数组的解决方案

问题背景

你当前的代码可以正确处理仅包含值类型(如Int)的结构体,但当结构体包含String类型时,直接拷贝结构体内存字节的方案会失效——因为String的实际内容存储在堆上,结构体本身仅持有指向堆内容的指针、长度和引用计数。当原结构体实例被销毁时,String的引用计数归零,堆上的内容会被释放,导致你存入字节数组的指针变成无效的野指针。

为什么原方案不可行

MemoryLayout<Test>.size获取的是结构体在栈上的内存大小,而String的真实数据并不在这片栈内存里。你拷贝的只是String的元数据(指针、长度、引用计数),而非实际字符串内容。一旦原结构体被销毁,这些元数据指向的堆内存会被回收,字节数组里的内容也就失去了意义。

解决方案

方案1:使用Codable序列化(推荐)

这是最安全可靠的方式,能完整序列化结构体的所有内容,包括String的实际值,而非内存指针。通过Codable协议可以将结构体转换为Data(本质就是字节数组),需要时再反序列化回来:

import Foundation

struct Test: Codable {
    var val: String
}

// 序列化结构体为字节数组(Data)
let test = Test(val: "Hello, Swift")
if let data = try? JSONEncoder().encode(test) {
    // data就是可存储/传输的字节数组
    print("序列化后的字节数:\(data.count)")
    
    // 反序列化还原结构体
    if let decodedTest = try? JSONDecoder().decode(Test.self, from: data) {
        print("还原后的字符串:\(decodedTest.val)")
    }
}

方案2:临时保留结构体实例(仅适用于短期使用)

如果只是临时需要字节数组,且能保证结构体实例在字节数组使用期间不被销毁,那么原代码可以正常工作——因为结构体存活时,String的引用计数不会归零,堆上的内容不会被释放:

var byteArray = [UInt8](repeating: 0, count: MemoryLayout<Test>.size)
var test = Test(val: "临时字符串") // 用var声明,确保实例不会被提前销毁

// 执行存储操作
store(&test, MemoryLayout<Test>.size)

// 在test实例被销毁前,byteArray中的内容是有效的
// 注意:test一旦被销毁,byteArray里的String相关指针立即失效,不能再使用

关于Unmanaged的说明

Unmanaged是专门用于类(引用类型)的内存管理工具,无法直接用于结构体。结构体是值类型,其生命周期由作用域决定,当结构体被销毁时,它包含的所有成员(包括String)都会被自动清理。你无法像保留类实例那样,用Unmanaged来强制延长结构体或其成员的生命周期。

内容的提问来源于stack exchange,提问作者Jomy

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.02 20:41:06