Rob
Rob

Reputation: 591

Large SwiftUI struct leading to memory error

I am still pretty new to iOS development. I have a (very?) large view that is causing a memory error. My body var looks something like this:

var body: some View {
        ZStack {
            Color(...)            
            Rectangle()
            ScrollView {
                VStack {
                    ZStack {
                        RoundedRectangle(...)
                        RoundedRectangle(...)
                                             
                        VStack {
                            ZStack{
                                Circle()                                
                                Circle()
                                Circle()
                                Image(...)
                                
                            }.overlay(
                                 HStack {
                                     // more stuff
                                 }
                            )                            
                        }

                        // many more views
                    }
                }
            }
        }
}

I added and removed views to confirm it was a problem related to having too many views. At some point, when I add another view, I get this (or similar) memory error:

Thread 1: EXC_BAD_ACCESS (code=2, address=0x16cd27fc8)

The address seems to be different each time but always starts with 0x16, which leads me to believe it's a stack overflow... I found this answer/question where they have a very similar problem and were able to solve it by moving large structs to the heap. I am not certain how exactly to accomplish this, even after looking at the example provided in the answer.

My main thought is that there must be a better way to organize my body in the first place that I am just missing. Is using the heap really necessary for rendering a view with a lot of children? Any help or advice is appreciated.

Upvotes: 2

Views: 697

Answers (3)

Rob
Rob

Reputation: 591

As suggested by @DarkDust in the comments, refactoring the large body into sub views fixed the issue. I took a large section of my body and made it a separate struct, then included that struct back in the body of my main struct.

Basically, changed from this:

struct MainStruct: View {
    var body: some View {
        ZStack {
            Color(...)            
            Rectangle()
            ScrollView {
                VStack {  // this VStack is the chunk we'll remove
                    ZStack {
                        RoundedRectangle(...)
                        RoundedRectangle(...)
                                         
                        VStack {
                            ZStack{
                                Circle()                                
                                Circle()
                                Circle()
                                Image(...)
                            
                            }.overlay(
                                 HStack {
                                     // more stuff
                                 }
                            )                            
                        }
                        // more views/subviews
                    }
                    // more views/subviews
                }
                // more views/subviews
            }
            // more views/subviews
        }
        // more views/subviews
    }
}

To this:

struct MainStruct: View {
    var body: some View {
        ZStack {
            Color(...)            
            Rectangle()
            ScrollView() {
                MyBigStruct() 
            }
            // more views/subviews
        }
        // more views/subviews
    }
}

struct MyBigStruct: View {
    var body: some View {
        VStack {
            ZStack {
                RoundedRectangle(...)
                RoundedRectangle(...)
                                             
                VStack {
                    ZStack{
                        Circle()                                
                        Circle()
                        Circle()
                        Image(...)
                    }.overlay(
                        HStack {
                            // more stuff
                        }
                    )                            
                }
                // more views/subviews
            }
            // more views/subviews
        }
        // more views/subviews
    }
}

Upvotes: 0

malhal
malhal

Reputation: 30551

body has a 10 View limit, break up body into as small custom subviews as possible (based on what let/@State var each uses).

Upvotes: 1

Use LazyVstack Instead Of Vstack it only loads the views we see on the screen.

var body: some View {
        ZStack {
            Color(...)            
            Rectangle()
            ScrollView {
//Vstack load the whole content at a time while LazyVstack load as its needed 
                LazyVStack{ 
                    ZStack {
                        RoundedRectangle(...)
                        RoundedRectangle(...)
                                             
                        VStack {
                            ZStack{
                                Circle()                                
                                Circle()
                                Circle()
                                Image(...)
                                
                            }.overlay(
                                 HStack {
                                     // more stuff
                                 }
                            )                            
                        }

                        // many more views
                    }
                }
            }
        }
}

Upvotes: 1

Related Questions