swift和swiftui_深入了解swiftui的状态和性能

本文深入探讨了SwiftUI中的视图、状态管理和性能优化,通过译文了解如何在SwiftUI开发中实现高效稳定的应用程序。
摘要由CSDN通过智能技术生成

swift和swiftui

Quite a few people have written articles on SwiftUI, SwiftUI state management, and on SwiftUI application architecture. And quite a few of those articles were written by people eager to take their favorite iOS application architecture and port it over to SwiftUI.

很少有人写过有关SwiftUI,SwiftUI状态管理和SwiftUI应用程序体系结构的文章。 这些文章中有很多是由渴望采用自己喜欢的iOS应用程序体系结构并将其移植到SwiftUI的人们撰写的。

Hence we’ve had articles telling us how to use MVVM with SwiftUI. Or VIPER. Or Clean. Or those that insist that we need to impose a React/Redux architecture on top of it.

因此,我们有文章告诉我们如何在SwiftUI中使用MVVM。 或VIPER。 或清洁。 或者那些坚持认为我们需要在其之上强加React / Redux架构的人。

But most of them have a problem.

但是大多数都存在问题。

You see, many of the architectures and methodologies that we’re attempting to inflict on SwiftUI were constructed and honed on applications built with UIKit and that relied on UIKit behaviors. Or worse, they’re architectures designed for other platforms and languages, like JavaScript and React.

您会看到,我们试图在SwiftUI上施加的许多架构和方法都是在使用UIKit构建的应用程序上构建和完善的,这些应用程序依赖于UIKit的行为。 更糟糕的是,它们是为其他平台和语言(例如JavaScript和React)设计的架构。

And the problem is… SwiftUI is not UIKit.

问题是…SwiftUI 不是 UIKit。

It doesn’t work the same, and it most definitely doesn’t behave the same.

它的工作原理不尽相同,并且绝对具有不同的表现。

This article will explore some of the mechanisms behind SwiftUI, and why even the words that we’re using to describe our application and its structure don’t mean what we think they mean.

本文将探讨SwiftUI背后的一些机制,以及为什么连我们用来描述应用程序及其结构的词也并不意味着我们认为它们意味着什么。

And why those misunderstandings can lead to performance problems, unexpected behavior, and even fatal errors in our applications.

以及这些误解为何会导致我们的应用程序出现性能问题,意外行为甚至致命错误的原因。

代码 (The Code)

Before we get started, I want to make it clear that the following conclusions are based on facts obtained from debugging, instrumenting, profiling, and logging actual SwiftUI applications.

在开始之前,我想明确指出以下结论是基于从实际SwiftUI应用程序的调试,检测,配置和日志记录中获得的事实。

The demonstration code in this article comes from a basic SwiftUI Master/Detail app to which I added a common Model-View-ViewModel architecture. I strongly suggest you download and run the code, interact with the program, and watch the logs as you do so.

本文中的演示代码来自基本的SwiftUI Master / Detail应用程序 ,我在其中添加了通用的Model-View-ViewModel体系结构。 我强烈建议您下载并运行代码,与程序进行交互,并在执行操作时查看日志。

This article is long, but a good portion of that length comes from the inclusion of some of that demonstration code and the resulting logs so you can better track the events unfolding as they occur and how those events support the article’s conclusions.

本文很长,但其中的很大一部分来自一些演示代码和生成的日志,因此您可以更好地跟踪事件发生时发生的事件以及这些事件如何支持本文的结论。

Let’s get started.

让我们开始吧。

没有视图 (There is no View)

If you’ve been an iOS developer for any length of time you have UIView and UIViewController baked into your brain.

如果您已经成为iOS开发人员有一段时间,那么UIViewUIViewController就会成为您的大脑。

You understand that views and view controllers are reference types and you understand UIKit’s rather extensive class heirarchy, from NSObject on up.

您了解视图和视图控制器是引用类型,并且从NSObject开始,您还了解UIKit相当广泛的类层次结构。

You understand how view controllers manage views, how they persist in the navigation stack, and all of the UIViewController lifecycle events. And you understand how views are constructed and added to subviews which are added to subviews and how they persist and are managed throughout the window-view-tree.

您将了解视图控制器如何管理视图,它们如何在导航堆栈中持久保存以及所有UIViewController生命周期事件。 并且您了解如何构造视图并将视图添加到添加到子视图的子视图中,以及它们如何持久化并在整个窗口视图树中进行管理。

Unfortunately, it’s this baked-in, ingrained knowledge of how UIKit-based development works that’s actually detrimental to understanding SwiftUI.

不幸的是,这种基于UIKit的开发工作方式的扎根,根深蒂固的知识实际上不利于理解SwiftUI。

Apple didn’t help much in this regard, as SwiftUI’s developers decided in their infinite wisdom to tell you that everything was a “View”. You make views of type View. Your view body returns some View. Even view modifiers return views.

Apple在这方面没有太大帮助,因为SwiftUI的开发人员以他们无穷的智慧决定告诉您一切都是“视图”。 您可以创建View类型的视图 。 您的视图主体返回一些 View 。 甚至视图修饰符也会返回视图。

But View is a term that makes long-time iOS developers think one thing when in actuality it’s something very, very, very different.

但是View是一个术语,它使长期的iOS开发人员实际上想到的是一件非常非常不同的事情。

UIKit is an imperative framework. We build our interface elements piece by piece, one element at a time, and then we wire everything together as best we can and shuffle data back and forth as needed.

UIKit是命令式框架。 我们一步一步地构建界面元素,然后一次将所有元素连接在一起,并根据需要来回移动数据。

SwiftUI, on the other hand, is a declarative framework. We use it to describe the user interface we want and then let SwiftUI build that interface for us based on our description.

另一方面,SwiftUI是声明性框架。 我们使用它来描述所需的用户界面,然后让SwiftUI根据我们的描述为我们构建该界面。

So please keep in mind that a SwiftUI View is not the resulting user interface view. It may not even end up being a view at all. A SwiftUI View is just a struct that contains a simple description of what we want our user interface to look like when it’s created and when it’s rendered.

因此,请记住,SwiftUI 视图 不是最终的用户界面视图。 它甚至可能根本就不是视图。 SwiftUI 视图只是一个结构,其中包含对我们希望用户界面在创建和呈现时的外观的简单描述。

A view may also have the ability to describe how we’re going to want that interface to eventually behave when the user interacts with it. Like when they tap a button.

视图还可以描述用户在与之交互时最终希望该界面如何表现。 就像当他们点击一个按钮时一样。

And that’s it. SwiftUI views are just definitions.

就是这样。 SwiftUI视图只是定义。

But old habits die hard. We look at a SwiftUI View, see that it’s a View, and in our heads we start thinking that our view definition is going to persist just like it would if it were a UIView. It won’t.

但是旧习惯很难消亡。 我们来看一个SwiftUI 视图 ,看到它是一个视图 ,并且在我们脑海中,我们开始认为我们的视图定义将像UIView一样持久化。 不会的

We think that it’s safe to hang view models and other classes off them like we do with UIViewController. It’s not.

我们认为,像使用UIViewController一样,将视图模型和其他类挂起是安全的。 不是。

We think we know when our “view” is instantiated, when its body variable is called, and how the interface is constructed from that definition. But we don’t.

我们认为我们知道何时实例化“视图”,何时调用其主体变量以及如何根据该定义构造接口。 但是我们没有。

And we may even think a view modifier modifies its view… I mean, that’s what it’s called, right? But again, the problem is that in many cases it doesn’t really do that either.

我们甚至可能认为视图修饰符会修改其视图……我的意思是,这就是所谓的,对吧? 但是,问题又来了,在很多情况下,它也没有做到这一点。

There’s a lot to unpack here, so let’s get started.

这里有很多要解压的东西,所以让我们开始吧。

那么,如果视图不是视图,那么它是什么? (So if a View is not a View, then what is it?)

SwiftUI is a struct-based, protocol-oriented, Domain-Specific-Language developed to describe user interfaces on Apple devices ranging from iPhones to iPads to Apple Watches to the Apple TV and even the Mac.

SwiftUI是一种基于结构的,面向协议的,特定于域的语言,旨在描述Apple设备上的用户界面,包括iPhone,iPad,Apple Watch,Apple TV甚至Mac。

In that sense, it’s platform agnostic. We give SwiftUI an idea of what we want, and by and large we let it do what’s appropriate when that code’s run on a specific platform. A Toggle view says we want a switch, but how that switch looks and even how it functions differs from iOS to macOS to the Apple TV.

从这个意义上讲,它与平台无关。 我们给SwiftUI一个我们想要的想法,总的来说,当代码在特定平台上运行时,我们让它做适当的事情。 切换视图表示我们需要一个开关,但是从iOS到macOS再到Apple TV,该开关的外观甚至功能如何都不同。

From an implementation standpoint, it’s important to remember that SwiftUI Views are lightweight value types that exist solely to define our interface.

从实现的角度来看,重要的是要记住,SwiftUI 视图是仅用于定义我们的接口的轻量值类型。

Every custom view we create has a body variable that, when called, returns another view. The returned view might be a primitive view type native to SwiftUI like Text or Image.

我们创建的每个自定义视图都有一个body变量,该变量在调用时会返回另一个视图。 返回的视图可能是SwiftUI固有的原始视图类型,例如TextImage

struct ItemDateView: View {
       
var date: Date
var body: some View {
Text("\(date, formatter: dateFormatter)")
}
}

Here, the description of ItemDateView’s interface is exceedingly simple. We just want to show the user a date formatted as text. The returned view could also be a container view like a List or VStack that tracks and manages a list of views.

在这里, ItemDateView界面的描述非常简单。 我们只想向用户显示一个格式化为文本的日期。 返回的视图也可以是容器视图,例如List或VStack ,用于跟踪和管理视图列表。

struct DetailContentView: View {
       
var model: DetailViewModel
var body: some View {
tracker {
VStack(spacing: 16) {
ItemDateView(item: model.item)
DetailStateView()
DetailBoilerplateView(text: model.boilerplate)
Spacer()
}
}
}
}

Here, our DetailContentView is also simple. We just want a vertical stack in which we’ll show the contents of our ItemDateView, a DetailStateView, and a DetailBoilerplateView, plus another primitive Spacer view.

在这里,我们的DetailContentView也很简单。 我们只需要一个垂直堆栈,即可在其中显示ItemDateView, DetailStateViewDetailBoilerplateView以及另一个原始的Spacer视图的内容。

Each one of those three custom views has its own body variable, which when called will again return some primitive view, a container view, or a custom view.

这三个自定义视图中的每一个都有其自己的body变量,该变量在被调用时将再次返回一些原始视图,容器视图或自定义视图。

In SwiftUI, we rinse and repeat and combine all of these views as needed until we’ve finally defined our entire application.

在SwiftUI中,我们将根据需要冲洗并重复并合并所有这些视图,直到最终定义了整个应用程序。

But note that nothing we’ve defined so far has generated a single user interface element.

但是请注意,到目前为止,我们定义的任何内容都没有生成单个用户界面元素。

Key Point #1: A SwiftUI View is not a view. It’s a description of a view.

关键点1:SwiftUI视图不是视图。 这是对视图的描述。

状态和视图图 (State and the View Graph)

You can’t do much with SwiftUI without also learning that it’s designed from the ground up to be state-driven.

在不了解SwiftUI是完全由状态驱动而设计的情况下,SwiftUI不能做太多事情。

That state can be as simple as a view property. Or it could be a @State variable, or an @Environment variable, or any of several other types provided for us by SwiftUI.

该状态可以与view属性一样简单。 也可以是@State变量或@Environment变量,或者是SwiftUI为我们提供的其他几种类型中的任何一种。

Each piece of state is the Source of Truth for some part of our application.

每个状态都是我们应用程序某些部分的真理

Image for post
WWDC19 “Data Flow Through SwiftUI” presentation
WWDC19“通过SwiftUI的数据流”演示

When our application is launched, the views we’ve defined are combined with the initial state of the application and together will eventually form a tree-like structure known as the view graph.

启动我们的应用程序时,我们定义的视图将与应用程序的初始状态结合在一起,并最终形成一个称为视图 的树状结构。

This graph defines how our application appears at that specific point in time.

此图定义了我们的应用程序在该特定时间点的显示方式。

应用启动 (Application Launch)

So let’s run our app and examine the process in detail. Most SwiftUI apps start with a ContentView that’s assigned as the root view of our application in our SceneDelegate. Or, in SwiftUI 2.0, in the main App view.

因此,让我们运行我们的应用程序并详细检查该过程。 大多数SwiftUI应用程序都以ContentView开头,该ContentViewSceneDelegate中被分配为应用程序的根视图。 或者,在SwiftUI 2.0中,在主应用程序视图中。

@main
struct AppStateDemo: App {
var body: some Scene {
return WindowGroup {
ContentView()
}
}
}

The struct defining ContentView will be instantiated when our application was launched and that particular instance of the view will exist for the lifetime of the entire application (or the lifetime of the scene, in this case).

启动我们的应用程序时,将实例化定义ContentView的结构,并且该视图的特定实例将在整个应用程序的生命周期(在这种情况下为场景的生命周期)中存在。

So let’s say that it’s finally time for our interface to be rendered and presented to the user. To accomplish this, SwiftUI will ask ContentView’s body variable to provide a set of views that describe the interface we want.

因此,可以说现在是时候将我们的界面呈现并呈现给用户了。 为此,SwiftUI将要求ContentView的 body变量提供一组描述所需接口的视图。

Here’s our ContentView.

这是我们的ContentView

struct ContentView: View {
       
@ObservedObject var master = MasterViewModel()
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值