tera
1
I was very surprised to know that SwiftUI initialises the NavigationLink destination view before I actually navigate inside that link! Is this a bug or by design and if so why?! Any way to have those subviews allocated only when user taps the link? It's the same behaviour with ScrollView + LazyVStack, and I need List anyway. The following app initialises MyView objects as their parent labels appear on the screen, and as I scroll the list down the more and more items are allocated and never deallocated!
class MyObject: ObservableObject {
var id: Int = -1
init(id: Int) {
print("MyObject init for \(id)")
}
deinit {
print("MyObject deinit for \(id)")
}
var text: String {
"Here you are \(id)"
}
}
struct MyView: View {
@ObservedObject private var someObject: MyObject
let id: Int
init(id: Int) {
self.id = id
_someObject = ObservedObject(initialValue: MyObject(id: id))
}
var body: some View {
Text(someObject.text)
}
}
struct ContentView: View {
var body: some View {
NavigationView {
List {
ForEach(0 ..< 10000) { id in
NavigationLink.init("Hello \(id)") {
MyView(id: id)
}
}
}
}
}
}
1 Like
Jon_Shier
(Jon Shier)
2
This has been the behavior since SwiftUI 1. There are various solutions floating around, including a LazyNavigationLink. As to why, I'm not sure we've ever gotten a clear explanation from Apple.
3 Likes
jflan
(John Flanagan)
3
Using @StateObject behave more closely to what you want.
class MyObject: ObservableObject {
var id: Int = -1
init(id: Int) {
self.id = id
print("MyObject init for \(id)")
}
deinit {
print("MyObject deinit for \(id)")
}
var text: String {
"Here you are \(id)"
}
}
struct MyView: View {
@StateObject private var someObject: MyObject
init(id: Int) {
_someObject = .init(wrappedValue: MyObject(id: id))
}
var body: some View {
Text(someObject.text)
}
}
struct ContentView: View {
var body: some View {
NavigationView {
List {
ForEach(0 ..< 10000) { id in
NavigationLink.init("Hello \(id)") {
MyView(id: id)
}
}
}
}
}
}
The initializer for StateObject uses @autoclosure @escaping to not actually instantiate MyObject until you follow the NavigationLink and it releases the old instance when you navigate out and back into a row again.
This is the complete output from scrolling down to row 123, tapping into and back out of row 123, and then tapping into row 124:
MyObject init for 123
MyObject init for 124
MyObject deinit for 123
2 Likes