视图更新后保留视图/数据模型的引用

2023-12-30

考虑我们有一个RootView and a DetailView. DetailView有它自己的 BindableObject,我们称之为它DetailViewModel我们有这样的场景:

  1. RootView可能会被某种全局事件更新,例如错过了 互联网连接或通过它自己的数据/视图模型
  2. When RootView处理事件是 内容已更新,这会导致新的结构DetailView到 被创造
  3. If DetailViewModel是由创建的DetailView在初始化时, 会有另一个参考DetailViewModel并且它的状态(例如选定的对象)将被错过

我们怎样才能避免这种情况呢?

  1. 将所有 ViewModel 存储为环境对象,这基本上是一个单例池。这种方法会导致在不使用不需要的对象时将它们存储在内存中
  2. 将所有 ViewModel 从根视图传递到其子视图和子视图的子视图(具有上述缺点 + 痛苦的依赖关系)
  3. 将 View 独立数据对象(也称为工作人员)存储为环境对象。在这种情况下,我们在哪里存储与模型相对应的视图相关状态?如果我们将它存储在 View 中,最终会出现交叉更改 @States 的情况,这是 SwiftUI 所禁止的
  4. 更好的方法?

抱歉,我没有提供任何代码。这个问题是关于 Swift UI 的架构概念,我们试图将其结合起来声明性结构 and 引用带有数据的对象.

目前,我没有看到一种方法来保留仅对应于适当视图的引用,并且不要将它们永远保留在当前状态的内存/环境中。

Update:

让我们添加一些代码来看看如果虚拟机是由它的视图创建的话会发生什么

import SwiftUI
import Combine

let trigger = Timer.publish(every: 2.0, on: .main, in: .default)

struct ContentView: View {

    @State var state: Date = Date()

    var body: some View {
        NavigationView {
            VStack {
                NavigationLink(destination: ContentDetailView(), label: {
                    Text("Navigation push")
                        .padding()
                        .background(Color.orange)
                })
                Text("\(state)")
                    .padding()
                    .background(Color.green)
                ContentDetailView()
            }
        }
        .onAppear {
            _ = trigger.connect()
        }
        .onReceive(trigger) { (date) in
            self.state = date
        }
    }
}

struct ContentDetailView: View {

    @ObservedObject var viewModel = ContentDetailViewModel()
    @State var once = false

    var body: some View {
        let vmdesc = "View model uuid:\n\(viewModel.uuid)"
        print("State of once: \(once)")
        print(vmdesc)
        return Text(vmdesc)
            .multilineTextAlignment(.center)
            .padding()
            .background(Color.blue)
            .onAppear {
                self.once = true
            }
    }
}

class ContentDetailViewModel: ObservableObject, Identifiable {
    let uuid = UUID()
}

更新2:

看来,如果我们将 ObservableObject 作为 @State 存储在视图中(而不是 ObservedObject),那么 View 会在 VM 上保留引用

@State var viewModel = ContentDetailViewModel()

有什么负面影响吗?我们可以这样使用它吗?

更新3:

看来如果ViewModel保留在View的@State中:

  1. 并且 ViewModel 通过具有强引用的闭包保留 - deinit 永远不会被执行 -> 内存泄漏
  2. 并且 ViewModel 通过弱引用闭包保留 - deinit 每次视图更新时都会调用,所有subs将被重置,但属性将相同

Mehhh...

更新4:

这种方法还允许您在绑定闭包中保留强引用

import Foundation
import Combine
import SwiftUI

/**
 static func instanceInView() -> UIViewController {
     let vm = ContentViewModel()
     let vc = UIHostingController(rootView: ContentView(viewModel: vm))
     vm.bind(uiViewController: vc)
     return vc
 }
 */
public protocol ViewModelProtocol: class {
    static func instanceInView() -> UIViewController
    var bindings: Set<AnyCancellable> { get set }
    func onAppear()
    func onDisappear()
}

extension ViewModelProtocol {

    func bind(uiViewController: UIViewController) {
        uiViewController.publisher(for: \.parent)
            .sink(receiveValue: { [weak self] (parent) in
                if parent == nil {
                    self?.bindings.cancel()
                }
            })
            .store(in: &bindings)
    }

}

struct ModelView<ViewModel: ViewModelProtocol>: UIViewControllerRepresentable {

    func makeUIViewController(context: UIViewControllerRepresentableContext<ModelView>) -> UIViewController {
        return ViewModel.instanceInView()
    }

    func updateUIViewController(_ uiViewController: UIViewController, context: UIViewControllerRepresentableContext<ModelView>) {
        //
    }
}
struct RootView: View {

    var body: some View {
        ModelView<ParkingViewModel>()
            .edgesIgnoringSafeArea(.vertical)
    }

}

Apple 表示我们应该使用 ObservableObject 来处理 SwiftUI 之外的数据。这意味着您必须自己管理数据源。

看起来单状态容器最适合 SwiftUI 架构。

typealias Reducer<State, Action> = (inout State, Action) -> Void

final class Store<State, Action>: ObservableObject {
 @Published private(set) var state: State

 private let reducer: Reducer<State, Action>

 init(initialState: State, reducer: @escaping Reducer<State, Action>) {
     self.state = initialState
     self.reducer = reducer
 }

 func send(_ action: Action) {
     reducer(&state, action)
 }
}

您可以将商店的实例传递到 SwiftUI 应用程序的环境中,它将在所有视图中可用,并将存储您的应用程序状态而不会丢失数据。

我写了一篇关于这种方法的博客文章,请查看它以获取更多信息https://swiftwithmajid.com/2019/09/18/redux-like-state-container-in-swiftui/ https://swiftwithmajid.com/2019/09/18/redux-like-state-container-in-swiftui/

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

视图更新后保留视图/数据模型的引用 的相关文章

  • NSAttributedString 的 AutoLayout 行高计算错误

    我的应用程序从 API 中提取 HTML 将其转换为NSAttributedString 为了允许可点击的链接 并将其写入自动布局表中的一行 问题是 每当我调用这种类型的单元格时 高度都会被错误计算并且内容会被截断 我尝试了不同的行高计算实
  • 未安装 Apple 的全球开发者关系 (WWDR) 中间证书

    我今天无法通过 Appcelerator Titanium 发布我的应用程序 AdHoc 我开始收到此错误 错误 未安装 Apple 的全球开发者关系 WWDR 中间证书 错误 这将阻止您为 iOS 设备构建应用程序或用于分发的软件包 我上
  • Xcode 9.2 的正确应用程序图标 (appicon) 命名约定是什么?

    我从以下网站下载了一组 23 个 iOS 应用程序图标https makeappicon com https makeappicon com具有以下文件名 email protected cdn cgi l email protection
  • 如何让 Chrome Cast 在 iOS 后台运行?

    我有一个简单的问题 当您进入 iPhone 的主屏幕并且不退出应用程序时 chrome Cast 设备会停止在屏幕上播放视频 当应用程序在后台运行时 我如何才能保持播放状态 如果您有一个视频应用程序并且它在投射设备中运行 您可能需要以下行为
  • 渐变方向从左到右

    我完全被这个问题难住了 它应该如此简单 以至于让我发疯 我正在使用这个苹果反射教程 苹果反射示例 http developer apple com library ios samplecode Reflection Listings MyV
  • Draggable Boxview 不更新 Xamarin

    我的第一个问题是框视图生成在左上角 而不是我指定的设计网格第 10 行和网格第 3 列 第二个问题在于可拖动视图 在代码本地可拖动视图的第一部分中 它正确地调用了触摸事件 但也许它没有在GUI中更新
  • SKScene和SKView的paused属性之间的区别

    我使用以下代码暂停 SKScene self paused YES 然而 根据这个answer https stackoverflow com a 21593852 2043580 by 安德烈 戈尔杰耶夫 https stackoverf
  • iOS - 在 UITabBar 上方获取所需的阴影

    我试图让我的标签栏阴影看起来像这张图片中看到的那样 这样做的最佳方法是什么 我正在使用 Objective C Thanks 您可以使用以下代码为任何 UI 对象提供阴影 tabBar layer shadowOffset CGSize w
  • 致命错误:在 flutter 中找不到“Flutter/Flutter.h”文件

    这是错误 在文件中包含来自 Users chetan pub cache hosted pub dartlang org webview flutter 1 0 7 ios Classes JavaScriptChannelHandler
  • WKWebView在后台,几个奇怪的断言

    我想找出iOS中当前浏览器的userAgent 所以在 Xcode 创建的默认项目中我添加了 import ViewController h import
  • 为什么我无法更改 UIBarButtonItem 的标题?

    我想改变UIBarButtonItem s title 但这段代码不起作用 void viewDidLoad self smay void smay AppDelegate apd AppDelegate UIApplication sha
  • 如何将 SCNPlane 颜色更改为透明颜色

    我正在开发一个 ARKit 项目 在水平面上点击时需要波纹动画效果 为此 我采用了 UIView 对象并将其作为 SCNPlane 对象材料的内容传递 我已将波纹动画添加到 UIView 对象 一切正常 但我无法将 SCNPlane 颜色更
  • 增量后清除推送通知徽章

    我正在研究 iPhone 中的推送通知 当我收到推送通知时 它在我的应用程序图标上显示 1 下次显示 2 3 4 如果我打开应用程序 它是 0 下次它应该是 1 2 3 4 但它显示最后一个数字和 1 我想在打开应用程序后重置推送通知徽章
  • UIView 周围的虚线边框

    如何在周围添加虚线边框UIView 像这样的东西 如果您喜欢子层 还有另一种方法 在您的自定义视图的 init 中 输入以下内容 border 是 ivar border CAShapeLayer layer border strokeCo
  • 视频中的图像/文本叠加 swift

    我正在使用 swift 在视频中使用图像叠加来实现水印效果 我正在使用AVFoundation为此 但不知何故我没有成功 以下是我的覆盖图像 文本的代码 let path NSBundle mainBundle pathForResourc
  • 带有图像的 UITableView 滚动非常慢[重复]

    这个问题在这里已经有答案了 可能的重复 带图像的表格视图 加载和滚动缓慢 https stackoverflow com questions 4071497 table view with images slow load and scro
  • 如何在没有 MFMessageComposeViewController 的情况下发送和接收短信?

    我想发送和接收短信而不显示MFMessageViewController从我的申请中 有人能告诉我这怎么可能吗 不可能 除非您使用第 3 方 api 发送 接收短信
  • 使用自定义格式将字符串转换为 NSDate [重复]

    这个问题在这里已经有答案了 可能的重复 NSString 到 NSDate https stackoverflow com questions 1353081 nsstring to nsdate iPhone 如何将 yyyyMMddTh
  • iOS 开发:如何强制 UIWebView 加载 Facebook 的非移动版本?

    我正在深入研究 iOS 开发 当我尝试在 UIWebView 中加载特定的 Facebook 粉丝页面时 它会加载该网站的移动版本 该版本仅加载粉丝页面的墙 而不是我需要加载的特定选项卡 在我的应用程序的 iPad 版本中 UIWebVie
  • 构建 iOS 应用程序后退出代码 1 错误

    我正在尝试使用 RestKit 构建我的项目 当我构建它时 我收到以下错误 我尝试使用 Apple LLVM 3 0 和 LLVM GCC 4 2 进行编译 两者都产生相同的结果 任何人有任何想法是什么导致了这个问题 Ld Users Ji

随机推荐

  • phpmyadmin 导出不带 DATABASE_NAME 或算法的视图

    当使用 phpmyadmin 导出 sql 转储时 它会创建如下所示的 VIEW 表 CREATE ALGORITHM UNDEFINED DEFINER root localhost SQL SECURITY DEFINER VIEW d
  • 使用 git,如何将一些未提交的更改从一个分支移动到不同文件夹中的另一个分支?

    我每天使用的同一个软件有两个不同的分支 然而 每次我检查另一个分支时 我的构建过程可能需要长达一个小时 为了解决这个问题 我刚刚在两个单独的文件夹中为每个分支检查了一次项目 我在一个分支中做了一些工作 并在提交之前意识到我位于错误的文件夹中
  • CodeIgniter 中分页的自动加载配置不起作用

    我正在尝试在我的 CI web 应用程序中实现分页 现在我将分页配置放入这样的配置文件中
  • 使用 VB.NET 执行存储过程

    这是我的程序 ALTER PROCEDURE sp addUser UserName nvarchar 50 Prenom nvarchar 50 Nom nvarchar 50 Mail nvarchar 50 Password char
  • “静态合成”是什么意思?

    我正在查看一些从 Java 字节码获得的反汇编代码 我看到一些声明如下 method static synthetic access 0 Lcom package Sample 我不明白是什么synthetic or access 0意思是
  • SQL Server 上的 Int PK 内连接与 Guid PK 内连接。执行计划

    我刚刚对 Int PK join 与 Guid PK 进行了一些测试 表结构和记录数如下所示 在这两种情况下 使用 EF4 进行 CRUD 操作的性能非常相似 众所周知 在连接中使用 Int PK 比字符串具有更好的性能 所以SQL Ser
  • 将 Service Worker 请求中的响应修改为图像

    您好 提前谢谢您 我的问题是关于使用响应网络请求服务工作者 我能够在文本或 html 的情况下处理它 但是当我尝试处理image我失败了 这是我的代码 self addEventListener fetch function event e
  • 本地主机上的 Firebase console.log?

    拿起去年春天我离开的 Firebase 项目 我不记得是怎么做的console log工作在firebase server 我有一个函数 有效 我尝试在其中编写一些调试信息 像这样 exports myfun functions https
  • 获取对象调用层次结构

    假设我有 3 个课程 class A void do A Check object call hierarchy class B void do B A a a do A class C void do C B b b do A 然后我打电
  • Facebook iOS SDK 3.2.1 - [NSError fberrorShouldNotifyUser]:无法识别的选择器发送到实例

    我刚刚将我的应用程序从 Facebook iOS SDK 3 1 升级到 3 2 1 并且我正在尝试利用 NSError 上的新 FBError 类别提供的新错误处理 代码在底部 它编译得很好 但是当发生 FB 错误时 我在运行时得到以下信
  • R从字符串中提取数字

    字符串将是 042 195 143 192 001 145 045 125 125 如何提取最后一组数字 195 192 145 125 125 Try v1 lt c 042 195 143 192 001 145 045 125 125
  • Arduino 上的 Timer1 导致串行打印无法工作

    运行下面的代码 当我从串行监视器向 Arduino 发送任何字符时 Arduino 不会打印 a 我认为timer1代码有问题 但它应该可以工作 因为这段代码是我的C课老师给我的 void setup Serial begin 115200
  • 在 Parquet 中使用嵌套数据类型有什么好处?

    在 Parquet 文件格式中使用嵌套数据类型是否会带来任何性能优势 AFAIK Parquet 文件通常是专门为查询服务创建的 例如Athena 因此创建这些值的过程也可以简单地展平这些值 从而允许更轻松的查询 更简单的模式并保留每列的列
  • 带 proguard 的 Kotlin AAR 库:如何仅保留类和方法名称?

    我正在使用 Kotlin 构建一个 android 库 aar 文件 我需要以第三方用户将看到类和方法名称的方式混淆代码 他必须能够使用它们 它们是公开的 但我需要隐藏 混淆代码本身 我尝试将此文件用于 myLibrary proguard
  • C++ 中的模块是什么?

    该术语是什么意思module在下面的句子中引用 不允许异常跨模块边界传播 这是规则 62C 编码标准 http www gotw ca publications c cs htm作者 赫伯 萨特和安德烈 亚历山德雷斯库 我现在已经阅读了这本
  • 想要 javax.swing 钩子告诉我层次结构中的哪个组件正在执行操作

    如何通过最少的代码丑化在 Swing 程序中编写一个调试挂钩 告诉我层次结构中的哪个组件实际上正在处理每个 KeyStroke 或鼠标单击 并执行在组件的操作映射中映射到它的操作 我们正在编写一个复杂的 GUI 了解这些信息将非常有用 放入
  • 如何在谷歌地图中放置两个距离500m的标记

    我们如何将两个标记放置在 500 米的距离处 假设第一个标记的 latLng 是伦敦 51 0 第二个标记放置在距离该标记 500 米的地方 我已经尝试过任何事情 但无法找到并回答它 任何想法 纬度为 60 海里 但显然经度超出了这个范围
  • WPF DataGrid 组样式

    I have the following DataGrid in WPF with two groups First group is a bool flag which represents if a person is active i
  • 使用画布的橡皮擦效果

    我有一个扩展视图的自定义视图类 我正在使用重写的 onDraw 方法在画布上涂鸦 我有一个 POJO 列表 其中包括 Path 和 Paint 借助它们我可以重新创建绘制的旧路径 如下所示 Override protected void o
  • 视图更新后保留视图/数据模型的引用

    考虑我们有一个RootView and a DetailView DetailView有它自己的 BindableObject 我们称之为它DetailViewModel我们有这样的场景 RootView可能会被某种全局事件更新 例如错过了