When building in Xcode 15.4 debug, only a part of the initial View for List is initialized. As you scroll, new ones are initialized, and old ones are destroyed.
When building the same code in Xcode 16.2, ALL Views are initialized first, and then immediately destroyed as you scroll, new ones are initialized, and old ones are destroyed.
MRE:
struct ContentView: View {
private let arr = Array(1...5555)
var body: some View {
List(arr, id: \.self) {
ListCellView(number: $0)
}
}
}
struct ListCellView: View {
private let number: Int
private let arr: [Int]
@StateObject private var vm: ListCellViewModel // Just to print init/deinit
init(number: Int) {
print(#function, Self.self, number)
self.arr = Array(0...number)
self.number = number
let vm = ListCellViewModel(number: number) // To see duplicates of init
self._vm = StateObject(wrappedValue: vm) // because here is @autoclosure
}
var body: some View {
let _ = print(#function, Self.self, number)
Text("\(number)")
}
}
class ListCellViewModel: ObservableObject {
private let number: Int
init(number: Int) {
print(#function, Self.self, number)
self.number = number
}
deinit {
print(#function, Self.self, number)
}
}
An example from a memory report with this code:
Fortunately, the behavior in release mode is the same as in Xcode 15.4. However, the double initialization of Views is still present and has not been fixed.
...
init(number:) ListCellView 42
init(number:) ListCellViewModel 42
init(number:) ListCellView 42
init(number:) ListCellViewModel 42
deinit ListCellViewModel 42
body ListCellView 42
Yes, unnecessary View initializations and extra body calls are "normal" for SwiftUI, but why do they here twice so that one is discarded immediately?