Swift 與 Objective-C

Swift class 與 struct:複製後還有哪些內容共用

比較值、類別參考與含有參考的結構,實際確認變更傳播範圍。struct不代表深層複製,也不會自動保證執行緒安全。

閱讀 2 分鐘
Swift class 與 struct:複製後還有哪些內容共用 封面圖

表示一個值的模型可先考慮struct;需要物件識別性或共用物件狀態時再選class。用struct包住物件,不會自動讓物件獨立或具備執行緒安全性。

原理與設計判斷

結構的指派會複製儲存的值。如果屬性存的是類別參考,複製的是參考,指向的物件仍然共用。這與深層複製、執行緒安全是不同問題。繼承或Objective-C物件API需求也會影響選擇。 第一手資料

重現程式碼

實驗依序複製Point的整數、Box的參考,以及持有Box的Envelope。我們分開觀察修改物件屬性與把副本的box換成新物件兩種操作,並以precondition檢查每個階段的原本與副本。

這個獨立範例已於 2026-10-23 使用 Apple Swift 6.4,以 Swift 6 語言模式在 arm64 macOS 目標環境執行。不需要網路或額外套件。

下載完整 Swift 檔案

請在下載檔案的資料夾執行。檢查不符時,precondition 會中止程式。請勿使用 -Ounchecked,因為它會移除這些檢查。

swift -swift-version 6 swift-class-vs-struct.swift
struct Point { var x: Int }
final class Box {
    var x: Int
    init(_ x: Int) { self.x = x }
}
struct Envelope { var box: Box }

let point = Point(x: 0)
var copiedPoint = point
copiedPoint.x = 10
precondition(point.x == 0 && copiedPoint.x == 10)

let box = Box(0)
let copiedReference = box
copiedReference.x = 10
precondition(box === copiedReference && box.x == 10)

let envelope = Envelope(box: Box(0))
var copiedEnvelope = envelope
copiedEnvelope.box.x = 20
precondition(envelope.box.x == 20)
copiedEnvelope.box = Box(30)
precondition(envelope.box.x == 20 && copiedEnvelope.box.x == 30)

print("struct=0,10 class=10,10 nested-reference=20,20 replacement=20,30")

執行結果與解讀

struct=0,10 class=10,10 nested-reference=20,20 replacement=20,30

Point分成0與10,Box參考則共用10。透過Envelope副本改動內部物件,兩邊都會看到20;但把副本的box換成Box(30)後,原本仍是20。屬性值獨立與參考物件共用必須分開理解。

適用範圍與限制

這是循序執行的複製語意實驗,沒有測量資料競爭或效能。若共用可變變數或內部參考,仍需檢查隔離與同步。Swift 6的Sendable檢查不等於只宣告一個struct。

接下來要確認的事

先列出模型複製後哪些狀態應一起變動。若參考共用並非預期,可考慮不可變值或明確的複製政策。需要有效率地複製大型儲存區時,可接著看CoW實驗。

延伸實驗

來源與驗證