SwiftUI - ObservableObject 性能问题

hun*_*ley 5 macos ios swift swiftui

当 SwiftUIView绑定到 时ObservableObject,当观察到的对象内发生任何更改时,视图会自动重新加载- 无论更改是否直接影响视图。

这似乎会导致非平凡应用程序出现严重的性能问题。看这个简单的例子:

// Our observed model
class User: ObservableObject {
    @Published var name = "Bob"
    @Published var imageResource = "IMAGE_RESOURCE"
}


// Name view
struct NameView: View {
    @EnvironmentObject var user: User
    
    var body: some View {
        print("Redrawing name")
        return TextField("Name", text: $user.name)
    }
}

// Image view - elsewhere in the app
struct ImageView: View {
    @EnvironmentObject var user: User
    
    var body: some View {
        print("Redrawing image")
        return Image(user.imageResource)
    }
}
Run Code Online (Sandbox Code Playgroud)

这里我们有两个不相关的视图,位于应用程序的不同部分。他们都观察到User环境提供的共享的变化。NameView允许您User通过 TextField编辑的名称。ImageView显示用户的个人资料图片。

截屏

问题:在每次击键时NameView所有观察到这一点的视图User都被迫重新加载它们的整个正文内容。这包括ImageView,这可能涉及一些昂贵的操作 - 例如下载/调整大图像的大小。

这可以在上面的示例中轻松证明,因为每次您在 TextField 中输入新字符时都会记录"Redrawing name""Redrawing image"

问题:我们如何改进 Observable/Environment 对象的使用,以避免不必要的视图重绘?有没有更好的方法来构建我们的数据模型?

编辑:

为了更好地说明为什么这可能是一个问题,假设ImageView不仅仅是显示静态图像。例如,它可能:

  • 异步加载图像,由子视图initonAppear方法触发
  • 包含运行动画
  • 支持拖放界面,需要本地状态管理

还有更多的例子,但这些是我在当前项目中遇到的。在这些情况中的每一种情况下,body重新计算视图都会导致丢弃状态和一些昂贵的操作被取消/重新启动。

并不是说这是 SwiftUI 中的“错误”——但如果有更好的方法来构建我们的应用程序,我还没有看到 Apple 或任何教程提到它。大多数示例似乎都倾向于自由使用 EnvironmentObject 而没有解决副作用。

rob*_*off 10

为什么ImageView需要整个User对象?

回答:没有。

将其更改为仅获取它需要的内容:

struct ImageView: View {
    var imageName: String

    var body: some View {
        print("Redrawing image")
        return Image(imageName)
    }
}

struct ContentView: View {
    @EnvironmentObject var user: User

    var body: some View {
        VStack {
            NameView()
            ImageView(imageName: user.imageResource)
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

当我点击键盘按键时输出:

Redrawing name
Redrawing image
Redrawing name
Redrawing name
Redrawing name
Redrawing name
Run Code Online (Sandbox Code Playgroud)

  • 在本例中,“self”是一个“ImageView”,它是一个具有一个属性“imageName”的“struct”。因此,如果“self.imageName”不改变,“self.body”也不能改变。 (2认同)