Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >SwiftUI @State @Published @ObservedObject 深入理解和使用

SwiftUI @State @Published @ObservedObject 深入理解和使用

作者头像
沙漠尽头的狼
发布于 2021-12-01 08:01:21
发布于 2021-12-01 08:01:21
3.4K01
代码可运行
举报
文章被收录于专栏:Dotnet9Dotnet9
运行总次数:1
代码可运行

1.SwiftUI 是Apple 新出面向未来、跨多端解决方案、声明式编程 SwiftUI最新版本 2.0 但是需要 IOS 14 支持,多数现在还用的是IOS 13 所以很多不完善的东西都用SwiftUIX 以及各种库代替,bug也是层出不穷

2.下面是鄙人对 @State @Published @ObservedObject 理解,如有不对,还请指出

1.@State 介绍

因为SwiftUI View 采用的是结构体,当创建想要更改属性的结构体方法时,我们需要添加mutating关键字,例如:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
mutating func doSomeWork()

然而,Swift不允许我们创建可变计算属性,这意味着我们不能编写mutating var body: some View——这是不允许的。

@State允许我们绕过结构体的限制:我们知道不能更改它们的属性,因为结构是固定的,但是@State允许SwiftUI将该值单独存储在可以修改的地方。

是的,这感觉有点像作弊,你可能想知道为什么我们不使用类-它们可以自由修改。但是相信我,这是值得的:随着你的进步,你会了解到SwiftUI经常破坏和重新创建你的结构体,所以保持它们的小而简单的结构对性能很重要。

提示:在SwiftUI中存储程序状态有几种方法,您将学习所有这些方法。@State是专门为存储在一个视图中的简单属性而设计的。因此,苹果建议我们向这些属性添加私有访问控制,比如:@State private var tapCount = 0。

2.@Published + @ObservedObject 介绍

@Published是SwiftUI最有用的包装之一,允许我们创建出能够被自动观察的对象属性,SwiftUI会自动监视这个属性,一旦发生了改变,会自动修改与该属性绑定的界面。

比如我们定义的数据结构Model,前提是 @Published 要在 ObservableObject 下使用 然后用 @ObservedObject 来引用这个对象,当然@State 不会报错,但是无法更新

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class BaseModel: ObservableObject{
    @Published var name:String = ""
}
struct ContentView: View{
  @ObservedObject var baseModel:BaseModel = BaseModel()
  var body: some View{
      Text("用户名\(baseModel.name)")
      Button(action: {
              baseModel.name  = "Renew"
            }, label: {
                Text("更新视图")
            })
  }
}

3.最重要的部分 (代码注释部分最为主要,务必看完)

虽然上面案例运行中什么都正常展示加载,但是到了实际项目中,却一堆bug,这是如何导致的,如果对 这三种状态跟View绑定的关系不了解,很可能给自己留下隐患

先来看组案例

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//// MASK - 先定义两个Model 继承 ObservableObject 
class WorkModel: ObservableObject {
    
    @Published var name = "name"
    
    @Published var count = 1
}
class UserModel: ObservableObject {
    
    @Published var nickname = "nickname"
    
    @Published var header = "http://www.baidu.com"
}
//// MASK - View显示层
struct ContentView: View {
    @ObservedObject var workModel:WorkModel = WorkModel()
        
    @ObservedObject var userModel:UserModel = UserModel()
    var body: some View {
        VStack{
            Text("work.count \(workModel.count)")
            Text("work.name \(workModel.name)")
            Text("user.nickname \(userModel.nickname)")
            Text("user.header \(userModel.header)")
            
            Button(action: {
               userModel.nickname = "Renew"
               userModel.header = "http://..."
               workModel.name = "work name"
                
               workModel.count += 1
            }, label: {
                Text("更新数据")
            })
        }
     }
}

不出意外上面代码点击按钮就会更新数据,但是如果我们有个包装类呢

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class WrapperModel: ObservableObject{
    
    @ObservedObject var workModel:WorkModel = WorkModel()
        
    @ObservedObject var userModel:UserModel = UserModel()
}
struct ContentView: View {
    @ObservedObject var wrapperModel:WrapperModel = WrapperModel()
    var body: some View {
        VStack{
            Text("work.count \(wrapperModel.workModel.count)")
            Text("work.name \(wrapperModel.workModel.name)")
            Text("user.nickname \(wrapperModel.userModel.nickname)")
            Text("work.header \(wrapperModel.userModel.header)")
            
            Button(action: {
               wrapperModel.userModel.nickname = "Renew"
               wrapperModel.userModel.header = "http://..."
               wrapperModel.workModel.name = "work name"
                
               wrapperModel.workModel.count += 1
            }, label: {
                Text("更新数据")
            })
        }
     }
}

这时候点击按钮还会更新数据吗,答案是否定的,那这个是为啥呀???

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
因为SwiftUI更新数据的前提是触发 
第一层 绑定的对象 wrapperModel下的属性(字段)发生更新才会调用视图层更新数据
但是 第一次下绑定的对象还绑定了 @ObservedObject 或者其他类型的对象呢?
还会触发第一次对象属性更新吗,答案是不能的
你可以在 didSet 事件里面捕捉,是捕捉不到的,所以视图是不会更新的,那这还有其他解决方案吗

有: 调用对象 wrapperModel.objectWillChange.send() 方法告诉View 层 我更新 但是这个就是绝对的了吗?:不是 如果层次再深一点的model 还是有bug,触发不了

4.总结以及解决方案

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/// 既然我们知道View 跟 状态绑定的关系
/// 是以第一继承ObservableObject 类 下的属性(字段)更新来更新视图的
/// 那我们可以给 ObservableObject 加一个 无关紧要的字段,然后编写一个方法,来通知更新
class BaseobservableObject: ObservableObject {
    ///
    /// 注意
    /// 接收 子类model 时候要用   @ObservedObject 不能用 @Published
    /// 因为SwiftUI 更新机制是当前对象有 @Published 字段更新 就会调用View视图进行更新
    /// 在BaseModel里面实现 notifyUpdate 更新当前对象 _lastUpdateTime 字段,实现自身全部字段更新
    @Published private var _lastUpdateTime: Date = Date()
    ///
    /// 通知更新
    public func notifyUpdate() {
        _lastUpdateTime = Date()
    }
}
/// 那当我们 包装类下的对象更新的时候
/// 可以直接 调用包装类 notifyUpdate() 方法更新当前对象属性,来达到更新View 的效果
/// 顾忌:如果多次调用 notifyUpdate() View会刷新两边吗
/// 答案是否定的,再一次函数栈里面 多次调用 notifyUpdate() View也只更新一次
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/// 当子类继承了 BaseobservableObject 对象
/// 那么该对象下面属性其实可以不需要在写 @ObservedObject 或者 @Published 了
/// 因为更新属性之后调用了 notifyUpdate() 达到了更新整个对象的效果,所以可以省略了

5.其他知识

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/// MASK - 实现一个基础Model类,其他Model继承该类
class BaseModel: ObservableObject {
    
   @Published var isLoading = false
}
class SonModel: BaseModel {
    
    @Published var name = "name"
    
    @Published var count = 1
}
struct ContentView: View {
    @ObservedObject var sonModel:SonModel = SonModel()
    var body: some View {
        VStack{
            Text("name \(sonModel.name)")
            Button(action: {
               sonModel.name = "Renew"
            }, label: {
                Text("加载")
            })
        }
     }
}
/// 问题来了,现在我View 层我直接引用 
/// 照说这时候应该 Text 提示信息是 name Renew 但是点击没反应
/// 啥原因,问题其实还是跟上面的问题有点相似
/// SonModel 不是直接继承于 ObservableObject 类的
/// 所以,直接继承 ObservableObject 下的属性(字段)没更新,就不会更新View
/// 最简单的解决办法就是 更新直接继承 ObservableObject(父对象) 里面的随便一个属性
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-08-28,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Dotnet9 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
SwiftUI-数据流
SwiftUI中的界面是严格数据驱动的:运行时界面的修改,只能通过修改数据来间接完成,而不是直接对界面进行修改操作。
YungFan
2020/02/18
10.2K1
SwiftUI-数据流
SwiftUI:使用 @EnvironmentObject 从环境中读取自定义值
SwiftUI的环境使我们可以使用来自外部的值,这对于读取Core Data上下文或视图的展示模式等很有用。但是我们也可以将自定义对象发送到环境中,并在以后将它们读出来,这使我们可以在复杂的应用程序中更轻松地共享数据。
韦弦zhy
2020/09/10
9.7K0
SwiftUI数据流之State&Binding
@State是一个属性包装器(property wrapper),被设计用来针对值类型进行状态管理;用于在Struct中mutable值类型
肉眼品世界
2020/11/11
4.1K0
SwiftUI数据流之State&Binding
SwiftUI 状态管理系统指南
SwiftUI与苹果之前的UI框架的区别不仅仅在于如何定义视图和其他UI组件,还在于如何在整个使用它的应用程序中管理视图层级的状态。
Swift社区
2022/07/05
5.1K0
避免 SwiftUI 视图的重复计算
随着近年来有关 SwiftUI 的文章与书籍越来越多,开发者应该都已经清楚地掌握了 —— “视图是状态的函数” 这一 SwiftUI 的基本概念。每个视图都有与其对应的状态,当状态变化时,SwiftUI 都将重新计算与其对应视图的 body 值。
东坡肘子
2022/08/03
9.3K0
避免 SwiftUI 视图的重复计算
探讨 SwiftUI 中的几个关键属性包装器
在这篇文章中,我们将探讨几个在 SwiftUI 开发中经常使用且至关重要的属性包装器。本文旨在提供对这些属性包装器的主要功能和使用注意事项的概述,而非详尽的使用指南。
东坡肘子
2023/12/21
3770
探讨 SwiftUI 中的几个关键属性包装器
ObservableObject研究
我是在去年阅读王巍写的《SwiftUI 与 Combine 编程》才第一次接触到单一数据源这一概念的。
东坡肘子
2022/07/28
2.4K0
ObservableObject研究
@StateObject 研究
在我之前的文章@State研究中我们探讨过@State,通过它,我们可以方便的将值类型数据作为View的Source of truth。在SwiftUI 1.0时代,如果想将引用类型作为source of truth,通常的方法是使用@EnvironmentObject或者 @ObservedObject。
东坡肘子
2022/07/28
1.2K0
深度解读 Observation —— SwiftUI 性能提升的新途径
在 WWDC 2023 中,苹果介绍了 Swift 标准库中的新成员:Observation 框架。它的出现有望缓解开发者长期面临的 SwiftUI 视图无效更新问题。本文将采取问答的方式,全面而详尽地探讨 Observation 框架,内容涉及其产生原因、使用方法、工作原理以及注意事项等。
东坡肘子
2023/07/08
6210
深度解读 Observation —— SwiftUI 性能提升的新途径
@State 研究
我在去年底使用了SwiftUI写了第一个 iOS app 健康笔记,这是我第一次接触响应式编程概念。在有了些基本的认识和尝试后,深深的被这种编程的思路所打动。不过,我在使用中也发现了一些奇怪的问题。我发现在视图(View)数量达到一定程度,随着数据量的增加,整个app的响应有些开始迟钝,变得有粘滞感、不跟手。app响应出现了问题一方面肯定和我的代码效率、数据结构设计欠佳有关;不过随着继续分析,发现其中也有很大部分原因来自于SwiftUI中所使用的响应式的实现方式。不恰当的使用,可能导致响应速度会随着数据量及View量的增加而大幅下降。通过一段时间的研究和分析,我打算用两篇文章来阐述这方面的问题,并尝试提供一个现阶段的使用思路。
东坡肘子
2022/07/28
3K0
@State 研究
SwiftUI-MVVM
最近看了斯坦福大学 2020 春季的 SwiftUI 课程,总结一下 SwiftUI 是如何支持 MVVM 设计模式的。
YungFan
2020/06/02
3K0
SwiftUI-MVVM
SwiftUI 与 Core Data —— 安全地响应数据
保证应用不因 Core Data 的原因导致意外崩溃是对开发者的起码要求。本文将介绍可能在视图中产生严重错误的原因,如何避免,以及在保证视图对数据变化实时响应的前提下如何为使用者提供更好、更准确的信息。由于本文会涉及大量前文中介绍的技巧和方法,因此最好一并阅读。
东坡肘子
2022/12/16
3.3K0
SwiftUI 与 Core Data —— 安全地响应数据
一段因 @State 注入机制所产生的“灵异代码”
本文将通过一段可复现的“灵异代码”,对 State 注入优化机制、模态视图( Sheet、FullScreenCover )内容的生成时机以及不同上下文( 相互独立的视图树 )之间的数据协调等问题进行探讨。
东坡肘子
2023/03/02
1.9K0
一段因 @State 注入机制所产生的“灵异代码”
Ask Apple 2022 与 SwiftUI 有关的问答(上)
Ask Apple 为开发者与苹果工程师创造了在 WWDC 之外进行直接交流的机会。本文对本次活动中与 SwiftUI 有关的一些问答进行了整理,并添加了一点个人见解。本文为上篇。
东坡肘子
2022/12/16
12.3K0
Ask Apple 2022 与 SwiftUI 有关的问答(上)
Ask Apple 2022 与 SwiftUI 有关的问答(下)
Ask Apple 为开发者与苹果工程师创造了在 WWDC 之外进行直接交流的机会。本文对本次活动中与 SwiftUI 有关的一些问答进行了整理,并添加了一点个人见解。本文为下篇。
东坡肘子
2022/12/16
14.8K0
Ask Apple 2022 与 SwiftUI 有关的问答(下)
在 SwiftUI 中创建自适应的程序化导航方案
随着苹果对 iPadOS 的不断投入,越来越多的开发者都希望自己的应用能够在 iPad 中有更好的表现。尤其当用户开启了台前调度( Stage Manager )功能后,应用对不同视觉大小模式的兼容能力就越发显得重要。本文将就如何创建可自适应不同尺寸模式的程序化导航方案这一内容进行探讨。
东坡肘子
2022/12/16
4.3K0
在 SwiftUI 中创建自适应的程序化导航方案
SwiftUI + Core Data App 的内存占用优化之旅
尽管 SwiftUI 的惰性容器以及 Core Data 都有各自的内存占用优化机制,但随着应用视图内容的复杂( 图文混排 ),越来越多的开发者遇到了内存占用巨大甚至由此导致 App 崩溃的情况。本文将通过对一个演示 App 进行逐步内存优化的方式( 由原先显示 100 条数据要占用 1.6 GB 内存,优化至显示数百条数据仅需 200 多 MB 内存 ),让读者对 SwiftUI 视图的存续期、惰性视图中子视图的生命周期、托管对象的惰值特性以及持久化存储协调器的行缓存等内容有更多的了解。
东坡肘子
2023/03/08
2.4K0
SwiftUI + Core Data App 的内存占用优化之旅
SwiftUI 4.0 的全新导航系统
长久以来,开发者对 SwiftUI 的导航系统颇有微词。受 NavigationView 的能力限制,开发者需要动用各种技巧乃至黑科技才能实现一些本应具备基本功能(例如:返回根视图、向堆栈添加任意视图、返回任意层级视图 、Deep Link 跳转等 )。SwiftUI 4.0( iOS 16+ 、macOS 13+ )对导航系统作出了重大改变,提供了以视图堆栈为管理对象的新 API ,让开发者可以轻松实现编程式导航。本文将对新的导航系统作以介绍。
东坡肘子
2022/07/28
10.4K0
SwiftUI 4.0 的全新导航系统
解析 SwiftUI 中两处由状态更新滞后引发的严重 Bug
众所周知,SwiftUI 是一个响应式框架,这意味着,当数据源发生变化时,框架会自动更新视图。同样,当我们想调整视图显示时,应直接对状态进行修改。但是,SwiftUI 中的一些系统控件并没有完全遵循响应式的设计原则,由此在某些情况下会出现严重的错误,影响用户体验,并使开发者无所适从。
东坡肘子
2023/09/01
7630
解析 SwiftUI 中两处由状态更新滞后引发的严重 Bug
TCA - SwiftUI 的救星?(一)
打算用几篇文章介绍一下 TCA (The Composable Architecture[1]),这是一种看起来非常契合 SwiftUI 的架构方式。
Swift社区
2021/12/20
3.3K0
TCA - SwiftUI 的救星?(一)
相关推荐
SwiftUI-数据流
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
查看详情【社区公告】 技术创作特训营有奖征文
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验