SwiftUI @State @Published @ObservedObject深い理解と使用

SwiftUI @State @Published @ObservedObject深い理解と使用

SwiftUIは、Appleの新しい未来志向のクロスマルチマルチソリューション、宣言型プログラミングです。

最后更新 2021/10/18 16:51
Renew全栈工程师
预计阅读 5 分钟
分类
Swift UI
标签
ObservedObject Published State
  1. SwiftUIは、Appleの新しい未来志向のクロスマルチマルチソリューション、宣言型プログラミングです。

SwiftUIの最新バージョン2.0が必要ですが、IOS 14サポートが必要です。ほとんどはまだIOS 13を使用しているので、多くの不完全なものがSwiftUIXや様々なライブラリに置き換えられ、バグも無限大です。

2.以下は、@State @Published @ObservedObjectについての私の理解ですが、間違っている場合は、お知らせください。

1.@の州のプレゼンテーション

SwiftUI Viewは構造体を使用するため、プロパティを変更する構造体メソッドを作成するときには、mutatingキーワードを追加する必要があります。

mutating func doSomeWork()

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

@Stateは構造体の制限をバイパスできます。構造体は固定されているため、プロパティは変更できませんが、@StateはSwiftUIが変更可能な場所に値を個別に保存することができます。

はい、これは少し不正行為のように感じます。なぜクラスを使わないのか不思議に思うかもしれません。しかし、それだけの価値があります。上達するにつれて、SwiftUIはしばしば構造体を破壊して再作成することがわかります。そのため、構造体を小さくシンプルに保つことがパフォーマンスにとって重要です。

ヒント:SwiftUIにプログラムの状態を格納する方法はいくつかありますが、それらをすべて学びます。Stateは、ビューに格納される単純なプロパティ用に特別に設計されています。そのため、Appleはこれらのプロパティにプライベートアクセス制御を追加することをお勧めします。例えば、@State private var tapCount = 0。

2.@さんPublished + @ObservedObjectのご紹介

@PublishedはSwiftUIの最も便利なラッパーの1つで、自動的に監視されるオブジェクトプロパティを作成することができます。SwiftUIは自動的に監視し、変更が行われると、そのプロパティにバインドされたインターフェイスを自動的に変更します。

例えば、@PublishedがObservableObjectの下で使用されると仮定して、モデルを定義します。 次に、@ObservedObjectで参照します。もちろん@Stateはエラーを報告しませんが、更新できません。

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.最も重要な部分 (コードコメントが最も重要な部分、必ず読んでください)

上記のケースでは、すべてが正常にロードされていますが、実際のプロジェクトでは、バグがたくさんあり、どのように引き起こされ、これら3つの状態とビューバインディングの関係を理解していない場合は、自分自身に危険を残す可能性があります。

*** 最初のケース **

//// 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("更新数据")
            })
        }
     }
}

ボタンをクリックするとデータが更新されますが、ラッパークラスがある場合はどうなりますか?

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("更新数据")
            })
        }
     }
}

ボタンをクリックしてデータを更新しますか?答えはノーですが、なぜですか?

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

がある

オブジェクトwrapperModel.objectWillChange.sendメソッドを呼び出して、Viewレイヤに更新を指示します。 しかし、これは絶対的ですか?より深いモデルにバグがあってもトリガーできません

4.概要と解決策

/// 既然我们知道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也只更新一次
/// 当子类继承了 BaseobservableObject 对象
/// 那么该对象下面属性其实可以不需要在写 @ObservedObject 或者 @Published 了
/// 因为更新属性之后调用了 notifyUpdate() 达到了更新整个对象的效果,所以可以省略了

5.他の知識

/// 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(父对象) 里面的随便一个属性
Keep Exploring

延伸阅读

更多文章
近期更新 2026/05/01

このサイトはついにAIで再構築されました。

Razor Pages、SEO、違法なキーワード検索フィルタリング、ロード最適化、コンテンツのホットアップデート、最小限のテストセット、ウェブストアの使用方法など、Code WFをAIでリファクタリングしました。

继续阅读
近期更新 2026/04/22

バージョン別の. NETサポート状況(250 7 0 7更新)

仮想マシンとテストマシンを使用して、各バージョンのオペレーティングシステムの. NETサポートをテストします。オペレーティングシステムのインストール後、対応するランタイムを測定し、スターダストエージェントをパスとして実行できます。

继续阅读