🕊

[Swift] Class と Struct の変数の didSet 挙動の違いについて

2022/02/26に公開

結論

  • ClassStruct を持つ変数について、didSet を実装する時は、挙動が異なるので注意が必要

共通コード

以下のような、適当な ClassStruct を要します。

// 🏫 Class
class Moge {
    var mogemoge: Int = 0 {
        didSet {
            print("mogemoge: \(mogemoge)")
        }
    }
    
    init() {}
    
    func update(mogemoge: Int) {
        self.mogemoge = mogemoge
    }
}

// 🏗 Struct
struct Fuga {
    var fugafuga: Int = 0 {
        didSet {
            print("fugafuga: \(fugafuga)")
        }
    }
    
    // struct の場合 `mutating` をつけないとコンパイルエラー⚠️
    mutating func update(fugafuga: Int) {
        self.fugafuga = fugafuga
    }
}

上記の Class または Struct を変数にもつ Class をさらに用意します。

class Hoge {
    // 🔢 Int の場合
    var hogehoge: Int = 0 {
        didSet {
            print("hogehoge: \(hogehoge)")
        }
    }
    
    // 🏫 Class の場合
    var moge: Moge = .init() {
        didSet {
            print("moge: \(moge), mogemoge: \(moge.mogemoge)")
        }
    }
    
    // 🏗 Struct の場合
    var fuga: Fuga = .init() {
        didSet {
            print("fuga: \(fuga), fugafuga: \(fuga.fugafuga)")
        }
    }
    
    init() {}
}

let hoge: Hoge = .init()

ここで、この hoge に対して、値の更新を走らせるとどうなるか実験してみます。

🔢 Int の場合

🔢 Int の場合
hoge.hogehoge = 1
// hogehoge: 1

当たり前の挙動になりました。

🏫 Class の場合

🏫 Class の場合
hoge.moge = Moge()
// moge: __lldb_expr_49.Moge, mogemoge: 0

hoge.moge.mogemoge = 2
// mogemoge: 2

hoge.moge.update(mogemoge: 3)
// mogemoge: 3

これもなんとなく想像の通りの結果だと思います。

🏗 Struct の場合

🏗 Struct の場合
hoge.fuga = Fuga()
// fuga: Fuga(fugafuga: 0), fugafuga: 0

hoge.fuga.fugafuga = 4
// fugafuga: 4
// fuga: Fuga(fugafuga: 4), fugafuga: 4 ← ❗️❗️

hoge.fuga.update(fugafuga: 5)
// fugafuga: 5
// fuga: Fuga(fugafuga: 5), fugafuga: 5 ← ❗️❗️

Struct の持っている変数の変更を実施した場合、Struct の場合は、Class と違って、変更された変数の didSet に加えて、さらに、hoge が持っている fugaStruct 自身の didSet も走っています。

このように ClassStruct を持つ変数について、didSet を実装する時は、挙動が異なるので注意が必要です。

考察

このような挙動の差は、Class が参照型であり、Struct が値型であること原因であると考えられます。

また、似たような話として、@PublishedCurrentValueSubject の挙動も ClassStruct で差があります。

以上になります。

GitHubで編集を提案

Discussion