🌾
[Swift] [Combine] @Published な変数として Struct 型かつ nil を許容したときの注意点
@Published
/ CurrentValueSubject
に Struct
型かつ nil
を許容したときの注意点
@Published
な変数、または、 CurrentValueSubject
の Output
に、Struct
型かつ nil
を許容すると、その .value
が nil
の場合でも、その型が持っている変数に値を代入すると、ストリームが流れます。
ちなみに、Class
場合はそのような挙動は起こりません。
このような挙動の差は、Class
が参照型であり、Struct
が値型であること原因であると考えられます。
知らないとハマりそうですね。。。(ハマったので記事にしました😭)
サンプルコード
以下、サンプルコードです。
Struct
と Class
の比較のために、適当な Struct
と Class
を用意します。
以下を共通のコードとします。
var cancellables = Set<AnyCancellable>()
// 🏗 Struct
struct Moge {
var mogemoge: Int = 0
init(mogemoge: Int) {
self.mogemoge = mogemoge
}
}
// 🏫 Class
class Fuga {
var fugafuga: Int = 0
init(fugafuga: Int) {
self.fugafuga = fugafuga
}
}
@Published
の場合
class Hoge {
// 🏗 Struct
@Published var moge: Moge? = .init(mogemoge: 0)
// 🏫 Class
@Published var fuga: Fuga? = .init(fugafuga: 0)
init() {}
}
let hoge: Hoge = .init()
🏗 Struct の場合
// 🏗 Struct
hoge.$moge
.dropFirst()
.sink { print("mogemoge: \(String(describing: $0?.mogemoge))") }
.store(in: &cancellables)
hoge.moge = Moge(mogemoge: 1)
hoge.moge?.mogemoge = 2
hoge.moge = nil
hoge.moge?.mogemoge = 3
hoge.moge?.mogemoge = 4
// (出力)
// mogemoge: Optional(1)
// mogemoge: Optional(2)
// mogemoge: nil
// mogemoge: nil ← え❗️❓
// mogemoge: nil ← え❗️❓
🏫 Class の場合
// 🏫 Class
hoge.$fuga
.dropFirst()
.sink { print("mogemoge: \(String(describing: $0?.fugafuga))") }
.store(in: &cancellables)
hoge.fuga = Fuga(fugafuga: 5)
hoge.fuga?.fugafuga = 6
hoge.fuga = nil
hoge.fuga?.fugafuga = 7
hoge.fuga?.fugafuga = 8
// (出力)
// fugafuga: Optional(5)
// fugafuga: nil
CurrentValueSubject
の場合
CurrentValueSubject
も @Puhlished
のときと同様の挙動になります。
class Hoge {
// 🏗 Struct
var mogePub: CurrentValueSubject<Moge?, Never> = .init(.init(mogemoge: 0))
// 🏫 Class
var fugaPub: CurrentValueSubject<Fuga?, Never> = .init(.init(fugafuga: 0))
init() {}
}
let hoge: Hoge = .init()
🏗 Struct の場合
// 🏗 Struct
hoge.mogePub
.dropFirst()
.sink { print("mogemoge: \(String(describing: $0?.mogemoge))") }
.store(in: &cancellables)
hoge.mogePub.value = Moge(mogemoge: 1)
hoge.mogePub.value?.mogemoge = 2
hoge.mogePub.value = nil
hoge.mogePub.value?.mogemoge = 3
hoge.mogePub.value?.mogemoge = 4
// (出力)
// mogemoge: Optional(1)
// mogemoge: Optional(2)
// mogemoge: nil
// mogemoge: nil ← え❗️❓
// mogemoge: nil ← え❗️❓
🏫 Class の場合
// 🏫 Class
hoge.fugaPub
.dropFirst()
.sink { print("fugafuga: \(String(describing: $0?.))") }
.store(in: &cancellables)
hoge.fugaPub.value = Fuga(fugafuga: 5)
hoge.fugaPub.value?.fugafuga = 6
hoge.fugaPub.value = nil
hoge.fugaPub.value?.fugafuga = 7
hoge.fugaPub.value?.fugafuga = 8
// (出力)
// fugafuga: Optional(5)
// fugafuga: nil
以上になります。
Discussion