🚅
SwiftUIにおける@State(struct)とObservableのパフォーマンスをInstruments比較
@State(struct), Observableのパフォーマンス比較
iOS 26でのパフォーマンスをXcode 26のInstrumentsのSwiftUI Profileを使って比較します。
ここでの@Stateはstructに付与された物に言及しておりObservable(class)に付与しているものを指していません。
// 例
@State var selectedId: Int?
@State var text: String = ""
@State var item: Item
シンプルなListで並べられたItemを追加、変更するようなViewを作成します。
選択されたセルは背景が赤色になるようにします。
-
追加(Creation):右上の追加ボタン -
変更(Update):選択状態

コード
コード
import SwiftUI
import Combine
struct Cell: View {
var value: Int
var selected: Bool
var body: some View {
Text("Value: \(value)")
.frame(maxWidth: .infinity, alignment: .leading)
.listRowBackground(selected ? Color.red : nil)
}
}
struct StateListView: View {
@State var items = Array(0..<10)
@State var selectedItem: Int = 0
var body: some View {
NavigationStack {
ScrollView {
LazyVStack {
ForEach(items, id: \.self) { item in
Cell(value: item, selected: selectedItem == item)
.contentShape(.rect)
.onTapGesture {
selectedItem = item
}
}
}
}
.toolbar {
Button {
items.append(items.max(by: { $0 < $1 })! + 1)
} label: {
Image(systemName: "plus")
}
}
}
}
}
struct ObservableListView: View {
@Observable
final class State {
var items = Array(0..<10)
var selectedItems: Int = 0
}
var state = State()
var body: some View {
NavigationStack {
ScrollView {
LazyVStack {
ForEach(state.items, id: \.self) { item in
Cell(value: item, selected: state.selectedItems == item)
.contentShape(.rect)
.onTapGesture {
state.selectedItems = item
}
}
}
}
.toolbar {
Button {
state.items.append(state.items.max(by: { $0 < $1 })! + 1)
} label: {
Image(systemName: "plus")
}
}
}
}
}
struct ObservableObjectListView: View {
final class State: ObservableObject {
@Published var items = Array(0..<10)
@Published var selectedItem: Int = 0
}
@StateObject var state = State()
var body: some View {
NavigationStack {
List {
ForEach(state.items, id: \.self) { item in
Cell(value: item, selected: state.selectedItem == item)
.contentShape(.rect)
.onTapGesture {
state.selectedItem = item
}
}
}
.toolbar {
Button {
state.items.append(state.items.max(by: { $0 < $1 })! + 1)
} label: {
Image(systemName: "plus")
}
}
}
}
}
Profileの結果
操作手順
- Cellの1, 2, 3を順にタップして選択されたCellを変更
- Addボタンを3回タップ
理想
-
List(Creation)は1回のみ -
List(Update)はセルを3回追加(Create)しているため3回 -
Cell(Creation)は初回の10回作成と追加の3回のため13回 -
Cell(Update)は選択したセルを3回選んで変更しているため、Onになった3回、Offになった3回の合計6回
結果
| Name | List(Creation) | List(Update) | Cell(Creation) | Cell(Update) |
|---|---|---|---|---|
| @State | 1 | 6 |
13 | 6 |
| ObservableObject | 1 | 6 |
13 | 6 |
| Observable | 1 | 3 |
13 | 6 |
| 理想 | 1 | 3 |
13 | 6 |


考察
List(Update)の結果が異なります。
Observableに比べて、@State、ObservableObjectは3回多いです。
本来はCellがCreate(追加)された3回のみListがUpdateされるべきですが、@State、ObservableObjectはCellのCreation時だけでなくCellのUpdate時にもListがUpdateされてしまっています。
下図の@State StateListView.selectedItemの3回が余計


結論
@StateではなくObservableで状態管理すると再描画パフォーマンスが良くなります。
これはListやScrollViewの違いに依存しません。
パフォーマンスの差が出る理由は@Stateはstructの変更のみ監視でき、structは変更があると全て再構築されてしまいますが、Observableはclassのため必要な部分のみ更新可能だからだと思います。
参考文献
Discussion