Does LazyVStack and LazyVGrid release views from memory inside a ScrollView?

I am using LazyVStack inside a ScrollView. I understand that lazy views are rendered only when they come into view. However, I haven’t heard much about memory deallocation.

I observed that in iOS 18 and later, when scrolling up, the bottom-most views are deallocated from memory, whereas in iOS 17, they are not (Example 1).

Additionally, I noticed a similar behavior when switching views using a switch. When switching views by pressing a button, the view was intermittently deinitialized. (Example 2).

Example 1)

struct ContentView: View {
    var body: some View {
        ScrollView {
            LazyVStack {
                ForEach(0..<40) { index in
                    CellView(index: index)
                }
            }
        }
        .padding()
    }
}

struct CellView: View {
    let index: Int
    @StateObject var viewModel = CellViewModel()
    var body: some View {
        Rectangle()
            .fill(Color.accentColor)
            .frame(width: 300, height: 300)
            .overlay {
                Text("\(index)")
            }
            .onAppear {
                viewModel.index = index
            }
    }
}

class CellViewModel: ObservableObject {
    @Published var index = 0
    
    init() {
        print("init")
    }
    
    deinit {
        print("\(index) deinit")
    }
}


#Preview {
    ContentView()
}

Example 2


struct ContentView: View {
    @State var index = 0
    var body: some View {
        LazyVStack {
            Button(action: {
                if index > 5 {
                 index = 0
                } else {
                    index += 1
                }
            }) {
                Text("plus index")
            }
            
            MidCellView(index: index)
        }
        .padding()
    }
}

struct MidCellView: View {
    let index: Int
    var body: some View {
        switch index {
        case 1:
            CellView(index: 1)
        case 2:
            CellView(index: 2)
        case 3:
            CellView(index: 3)
        case 4:
            CellView(index: 4)
        default:
            CellView(index: 0)
        }
    }
}

struct CellView: View {
    let index: Int
    @StateObject var viewModel = CellViewModel()
    var body: some View {
        Rectangle()
            .fill(Color.accentColor)
            .frame(width: 300, height: 300)
            .overlay {
                Text("\(index)")
            }
            .onAppear {
                viewModel.index = index
            }
    }
}

class CellViewModel: ObservableObject {
    @Published var index = 0
    
    init() {
        print("init")
    }
    
    deinit {
        print("\(index) deinit")
    }
}
--------------------
init
init
init
init
init
2 deinit
3 deinit
4 deinit
init

Correct, Lazy Stack, manages memory footprint by only loading views incrementally as it becomes visible.

Keep in mind that onAppear/onDisappear do not equate visibility but when a view is added or removed from the view hierarchy.

@DTS Engineer , but the question was:

Does LazyVStack and LazyVGrid release views from memory inside a ScrollView?

in iOS 18

And not about loading views incrementally.

With such a big change, why not make it a separate component? Or at least add a parameter to existing? For example, so it looks like this:

ReusableVStack { ... }
// or
LazyVStack(reuseViews: true) { ... }
Does LazyVStack and LazyVGrid release views from memory inside a ScrollView?
 
 
Q