为什么清空 NavigationStack 路径后对象仍在内存中?

2024-02-14

我正在尝试实现一个协调器来管理流程。状态存储在 CoordinatorStore 内。有 2 个用于管理流程的 @Published 属性。这screen属性控制当前显示哪个视图以及path控制堆栈视图的导航堆栈。实施细节可以在下面找到。

根据当前实施情况以及采取以下行动后:showA -> showB -> 显示首字母 -> 转到堆栈

我希望 StoreS 和 Store 将从内存中释放,因为path,通过枚举关联值保存 StoreS 和 Store ,被清空。

但这种情况并没有发生,如果我再次重复这些操作,内存中就会有 2 个 StoreA 和 2 个 StoreB ,依此类推。我错过了什么吗?

在执行初始操作集后,我还将附上内存调试器快照的屏幕截图。

enum Path: Hashable {
    case a(StoreA)
    case b(StoreB)
}

enum Screen {
    case initial
    case stack
}

final class CoordinatorStore: ObservableObject {
    @Published var path: [Path] = []
    @Published var screen: Screen = .stack
    
    func showA() {
        let store = StoreA()
        path.append(.a(store))
    }
    
    func showB() {
        let store = StoreB()
        path.append(.b(store))
    }
    
    func showInitial() {
        path = []
        screen = .initial
    }
    
    func showStack() {
        screen = .stack
    }
}
struct Coordinator: View {
    @ObservedObject var store: CoordinatorStore
    
    var body: some View {
        switch store.screen {
        case .initial: initial
        case .stack: stack
        }
    }
    
    var stack: some View {
        NavigationStack(path: $store.path) {
            VStack {
                Text("Root")
            }
            .toolbar {
                Button(action: self.store.showA) {
                    Text("Push A")
                }
            }
            .navigationDestination(for: Path.self) { path in
                switch path {
                case .a(let store):
                    ViewA(store: store)
                        .toolbar {
                            Button(action: self.store.showB) {
                                Text("Push B")
                            }
                        }
                case .b(let store):
                    ViewB(store: store)
                        .toolbar {
                            Button(action: self.store.showInitial) {
                                Text("Show Initial")
                            }
                        }
                }
            }
        }
    }
    
    var initial: some View {
        VStack {
            Text("Initial")
            Button(action: store.showStack) {
                Text("Go to Stack")
            }
        }
    }
}
struct ViewA: View {
    @ObservedObject var store: StoreA
    
    var body: some View {
        Text("View A")
    }
}

final class StoreA: NSObject, ObservableObject {
    deinit {
        print("Deinit: \(String(describing: self))")
    }
}
struct ViewB: View {
    @ObservedObject var store: StoreB
    
    var body: some View {
        Text("View B")
    }
}

final class StoreB: NSObject, ObservableObject {
    deinit {
        print("Deinit: \(String(describing: self))")
    }
}

我相信这与以下内容相关但不完全相同:

  • 与新的导航堆栈结合时发现 @State 的奇怪行为 - 这是一个错误还是我做错了? https://stackoverflow.com/questions/73885353/found-a-strange-behaviour-of-state-when-combined-to-the-new-navigation-stack/

The Navigationapi 似乎优先考虑效率(初始化很昂贵)并且屏幕上必须始终显示某些内容。它似乎不会取消初始化已经消失的视图,直到它初始化并出现替换视图。

这可能会导致内存泄漏(我相信 https://developer.apple.com/forums/thread/716804)如果你尝试管理Navigation框架视图具有导航框架之外的内容,但只要Navigation框架仍然负责,事情最终将被取消,但直到新视图被初始化。

新版本

此版本使用一个协调器,但保留初始与主应用程序路径的单独枚举和视图。


import Foundation
import SwiftUI

enum AppSceneTvTe:Hashable {
    case setup
    case app
}

enum PathTvTeOptions: Hashable {
    case optionA(OptionAVM)
    case optionB(OptionBVM)
}
struct SplashTVTEView: View {
    @StateObject var oneCoordinator = CoordinatorTvTe()
    
    var body: some View {
        NavigationStack(path: $oneCoordinator.path) {
            splash
                .navigationDestination(for: AppSceneTvTe.self) { scene in
                switch scene {
                case .app:
                    SplashTvTeAppRootView().environmentObject(oneCoordinator)
                    
                default:
                    splash
                }
                
            }
        }
    }
    
    var splash: some View {
        VStack {
            Text("Splash Page")
            Button(action:navigateToApp) {
                Text("Go App Root")
            }
        }.navigationBarBackButtonHidden(true)
    }
    
    func navigateToApp() {
        oneCoordinator.showStack()
    }
}
final class CoordinatorTvTe: ObservableObject {
    @Published var path = NavigationPath()

    
    func showA() {
        path.append(PathTvTeOptions.optionA(OptionAVM()))
    }
    
    func showB() {
        path.append(PathTvTeOptions.optionB(OptionBVM()))
    }
    
    func showInitial() {
        unwindAll()
        //path = NavigationPath()
    }
    
    func showStack() {
        path = NavigationPath()
        path.append(AppSceneTvTe.app)
    }
    
    func unwindAll() {
        while !path.isEmpty {
            path.removeLast()
        }
    }
}
struct SplashTvTeAppRootView: View {
    @EnvironmentObject var navigation: CoordinatorTvTe

    
    var body: some View {
      
            VStack {
                Text("Real Root")
            }
            .navigationBarBackButtonHidden(true)
            .toolbar {
                Button(action: self.navigation.showA) {
                    Text("Push A")
                }
            }
            .navigationDestination(for: PathTvTeOptions.self) { path in
                switch path {
                case .optionA(let vm):
                    OptionAView(vm: vm)
                        .toolbar {
                            Button(action: self.navigation.showB) {
                                Text("Push B")
                            }
                        }
                case .optionB(let vm):
                    OptionBView(vm: vm)
                        .toolbar {
                            Button(action: self.navigation.showInitial) {
                                Text("Show Initial")
                            }
                        }
                }
            }

    }
    
}


旧版本

目前解决这个问题的方法是将其全部保留在导航堆栈中,这样就没有单独的场景与路径。

此代码使用布尔值来控制初始屏幕,但它可能是路径选项之一 - 这是注释掉的代码。

编辑添加:当您尝试使初始状态为真时,用完布尔解决方案会变得很奇怪。堆栈一直获胜,所以我把它拿出来了。

enum Path: Hashable {
    case initial
    case a(StoreA)
    case b(StoreB)
}

final class CoordinatorStore: ObservableObject {
    @Published var path: [Path] = [.initial]
    
    func showA() {
        let store = StoreA()
        path.append(.a(store))
    }
    
    func showB() {
        let store = StoreB()
        path.append(.b(store))
    }
    
    func showInitial() {
        path = []
        path.append(.inital)

    }
    
    func showStack() { 
        path = []
    }
}
struct Coordinator: View {
    @ObservedObject var store: CoordinatorStore

    
    var body: some View {
        NavigationStack(path: $store.path) {
            VStack {
                Text("Real Root")
            }
            .toolbar {
                Button(action: self.store.showA) {
                    Text("Push A")
                }
            }
            .navigationDestination(for: Path.self) { path in
                switch path {
                case .a(let store):
                    ViewA(store: store)
                        .toolbar {
                            Button(action: self.store.showB) {
                                Text("Push B")
                            }
                        }
                case .b(let store):
                    ViewB(store: store)
                        .toolbar {
                            Button(action: self.store.showInitial) {
                                Text("Show Initial")
                            }
                        }
                case .initial:
                    initial
                }

            }
        }
    }
    
    var initial: some View {
        VStack {
            Text("Initial")
            Button(action: store.showStack) {
                Text("Go to Stack")
            }
        }.navigationBarBackButtonHidden(true)
    }
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

为什么清空 NavigationStack 路径后对象仍在内存中? 的相关文章

  • 使用 iOS 8 自定义键盘发送图像?

    我一直在为 iOS 8 开发自定义键盘 但在尝试使用键盘发送图像时偶然发现了一个问题 我做了一些研究 似乎没有一种简单的方法可以做到这一点UITextDocumentProxy因为只有NSStrings被允许 我是否忽略了使用自定义键盘发送
  • 如何打开定位服务

    当有人第一次拒绝时 如何从实际应用程序重新打开定位服务 我可以选择关闭或打开它 您只能提示他们在屏幕上打开定位服务 如下所示 UIApplication sharedApplication openURL NSURL URLWithStri
  • 如何在 SwiftUI 中仅使用 ForEach 而不是列表来滑动删除

    我正在 SwiftUI 中使用 ForEach 制作自定义列表 我的目标是进行滑动删除手势 而不是将 ForEach 嵌入到列表中 到目前为止 这是我的代码 import SwiftUI struct ContentView View le
  • 确定 SceneKit 中 SKVideoNode 的视频大小/长宽比

    如何从 AVPlayer 获取视频的视频大小来设置节点的几何大小 例如 我有一个具有宽度和高度的 SCNPlane let planeGeo SCNPlane width 5 height 5 所以现在我实例化我的视频播放器 let vid
  • 如何在 Firebase 控制台中使用 Apple 新的 APN .p8 证书

    随着最近 Apple 开发者帐户的升级 我面临着一个困难 在尝试创建推送通知证书时 它为我提供了 p8 证书 而不是可以导出到 p12 的 APNs 证书 Firebase 控制台仅接受 p12 证书 那么我如何从这些新的 p8 证书中获取
  • 模块未使用库演化支持进行编译;使用它意味着无法保证二进制兼容性

    最近我遇到了 SDK 的编译时警告 这是否意味着它不是使用目标设置 构建用于分发的库 构建的 你需要设置Build Libraries for Distribution在项目中Build Settings to No然后它就会消失
  • 在 WKWebView 中禁用放大手势

    我正在寻找一种方法来禁用 WKWebView 的 iOS 实现上的 捏合缩放 放大手势 OS X 有一个 magnification BOOL 属性 但在 iOS 上似乎不可用 WKWebView h if TARGET OS IPHONE
  • 在横向中自动调整 UITableCells 内容的大小

    在 UITableView 中 我通过 UILabels 将内容添加到单元格中 定义最佳尺寸 与单元格宽度允许的一样大 我注意到只有tableView contentSize width是可靠的 因为cell contentView bou
  • Parse.com 从相关 PFObject 获取 PFUser

    我正在将照片保存为 PFObject 解析 并使用 PFUser currentUser 用户 ID 作为其键之一 我想在表格视图中显示照片以及该 PFUser 的详细信息 但是当我尝试获取用户时 PFUser user self phot
  • 如何知道我的应用程序使用了多少 iCloud 空间?

    有没有办法查看我的应用程序正在备份到 iCloud 的内容以及它消耗了多少内存 Settings gt iCloud gt Storage Backup gt Manage Storage将显示正在备份的总计内容 iOS 会备份位于应用程序
  • 导入 RNCryptor 后架构 armv7 的未定义符号

    我导入了 RNCryptor 可以在这里找到 https github com rnapier RNCryptor https github com rnapier RNCryptor进入我的应用程序 但是 我在日志中收到了三个错误 Und
  • UIScrollView setZoomScale 将应用的旋转设置回零

    我已经从事地图替换工作很长一段时间了 整个事情的工作原理是UIScrollView由一个支持CATiledLayer 为了旋转我的地图 我旋转图层本身 使用CATransform3DMakeRotation 到目前为止效果很好 但如果我打电
  • 在 Swift 中从 UIScrollView 创建 PDF 文件

    我想从 UIScrollView 的内容创建一个 PDF 文件 func createPdfFromView aView UIView saveToDocumentsWithFileName fileName String let pdfD
  • 覆盖层不与 UITableView 一起滚动 - iOS

    我有一个 UITableView 类 它使用以下方法在转到下一个屏幕时调用加载覆盖 问题是这个加载屏幕不随列表滚动 所以如果你滚动一点并单击某些东西 加载屏幕不会显示 因为它位于顶部 如何让加载屏幕始终保持在 UITableView 的顶部
  • 减少 CoreData 的调试输出?

    我正在开发一个使用 CoreData 的 iOS macOS 项目 它工作正常 但它会向控制台输出大量调试信息 这使得控制台无法使用 因为我的打印语句隐藏在所有与 CoreData 相关的内容中 我有一个非常简单的 CoreData 设置
  • 推送动画,没有阴影和停电

    我有一个简单的iOS NavigationController基于应用程序 二UICollectionViews 相继 如果元素打开 第一个合集 被点击时 第二集 将被打开 非常简单 重要的提示 Both UICollectionViews
  • 哪些 Flutter 插件或功能可以利用外部 iOS/Android 显示器来显示与主显示器不同的内容

    我正在构建一个跨平台应用程序 需要在外部显示器上显示不同的视图 通常通过连接到 LCD 投影仪的 HDMI 适配器电缆连接 Flutter 是否能够在内置的外部显示器上显示不同的屏幕 在现有的 Flutter 插件中还是使用现有的 Flut
  • 在 iOS 7 Safari 中,如何区分通过边缘滑动与后退/前进按钮的 popstate 事件?

    在 iOS 7 Safari 中 现在有两种后退 前进导航方式 使用底部的传统后退 前进按钮箭头或从屏幕边缘滑动 我正在使用动画在 ajax 应用程序中的页面之间进行转换 但如果用户通过边缘滑动进行导航 我不想触发该转换 因为这本身就是一个
  • Swift 中的 import 语句是否有相关成本?

    阅读字符串宣言 我看到一个段落 https github com apple swift blob master docs StringManifesto md batteries included关于避免Foundation不需要的时候导
  • iOS - UITableViewCell 使文本加粗

    我有一个字符串 NSString userInfo James Johnson james 我想做的就是大胆James Johnson并保留 james正常字体 所以我尝试过的是使用NSAttributedString但为了完成这个过程 我

随机推荐

  • 标签内的图像和文本

    这是生成的 html asp net 删除了一些客户端识别详细信息 在 Windows XP IE 7 中 单击图像不会执行任何操作 单击文本执行超链接 右键单击任意位置 然后选择open in new window or open也有效
  • Django:如何使用另一个应用程序中的模型

    我有两个应用程序 homepage and blog 我有一个模型Post在应用程序中blog 我可以将此模型用于应用程序blog但不适用于应用程序homepage 我如何在应用程序中使用这个模型homepage 我想在主页中显示我最近的一
  • 立即检测 iOS 方向变化

    我有一个游戏 其中设备的方向会影响游戏的状态 用户必须在横向 纵向和反向横向方向之间快速切换 到目前为止 我一直在通过以下方式注册游戏以获得方向通知 UIDevice currentDevice beginGeneratingDeviceO
  • jQuery 按值选择选项元素

    我有一个由 span 元素包裹的 select 元素 我不允许使用 select id 但可以使用 span id 我正在尝试编写一个 javascript jquery 函数 其中输入是数字 i 它是 select 选项的值之一 该功能会
  • R 在 Leaflet 中使用热图

    我有一个运行 Shiny 的 Linux 机器 我正在尝试根据演示获取传单运行的代码here https rpubs com bhaskarvk leaflet heatmap and here http leaflet github io
  • getRealPath() 返回 false(Laravel 5.2 和图像干预)

    上传图片时 getRealPath 总是返回false image file request gt file image file image file gt getRealPath gt FALSE 这是结果dd image file 另
  • 如何将 char 数组定义为常量?

    这里是 C C 菜鸟 我已经在头文件中定义了它 typedef unsigned char BitChar 9 8 data bytes chars and one width byte char extern BitChar BitFon
  • 使用 XCode 增加堆栈大小

    我在Linux上开发了一个命令行应用程序 需要增加其堆栈 在 Linux 上我只是使用了解决方法 ulimit s unlimited在运行程序之前 在 Mac OS X 上 使用 G 命令行 我添加到编译选项 Wl stack size
  • 使用代码清理时在 Resharper 中禁用/自定义注释(重新)格式化

    有没有办法定义如何使用 Resharper 6 清理 注释 我没有找到解决这个问题的方法 代码清理之前
  • 为什么 IntelliSense 无法将我的查询所选项目识别为元素?

    Problem 我试图弄清楚为什么当我使用以下命令访问 HTML 元素时 VSCode 的 IntelliSense 不会建议元素对象属性 document querySelector 每当我使用document querySelector
  • 重试 Javascript.Promise.reject 有限次数或直到成功

    我有一个函数说myMainFunction从客户端调用 然后调用mypromisified功能 设想 mypromisified函数可能会间歇性失败 我需要延迟 以指数增长 调用此函数 直到成功或达到最大尝试次数 到目前为止我所拥有的 以下
  • icmp 端口不可达错误消息

    我正在将 UDP 数据包从一台 PC 发送到另一台 PC 我正在使用 Wire Shark 观看整个活动 我注意到有一段时间数据包从一个系统到另一个系统的传输很顺利 然后突然间ICMP有错误的数据包 port unreachable 开始出
  • Bash 脚本中的这一行是如何工作的?

    我试图弄清楚顶部的某一行代码是如何BASH 手册页选项卡补全脚本 https github com scop bash completion blob 52315ef2ceb3f8e6b7fe45a09f8df24b73394da4 com
  • 是否可以通过 GitHub 的 Web 界面恢复提交

    我正在 Chromebook 上教授 HTML 课程 我们使用 GitHub 进行修订控制 我想向我的学生展示如何恢复提交 由于我们在 Chromebook 上没有 shell 访问权限 因此我希望通过 GitHub 网站找到一种在线执行此
  • Laravel 5.2 - pluck() 方法返回数组

    我正在尝试升级我的项目 L5 1 gt L5 2 在升级指南 http laravel com docs 5 2 upgrade upgrade 5 2 0有一件事我不清楚 The listsCollection 上的方法 查询生成器和 E
  • 如何使用 datetime.time 绘图

    我有 HH MM SS 格式的时间戳列表 并希望使用 datetime time 绘制某些值 看来 python 不喜欢我这样做的方式 有人可以帮忙吗 import datetime import matplotlib pyplot as
  • 我如何告诉 Masonry 组件它的单元格已经改变了高度?

    我有一个反应组件 它以网格模式显示一维项目列表 每个项目都具有相同的宽度和高度 并且它们位于三列内 可能有很多这样的东西 所以我决定使用 Masonry 组件来尝试一下 React virtualized 该组件似乎是专门为这个用例而编写的
  • 如何在 C# 中创建 X509Certificate2 时更改颁发者名称

    我正在我的 C net 应用程序中创建 X509Certificate2 证书 创建证书时 如何设置颁发者名称 目前发行人名称与主体名称相同 请帮忙 Hmm 终于我用了充气城堡 dll创建证书 使用它 有一种方法可以设置颁发者名称 这是要生
  • --ntasks 或 -ntasks 在 SLURM 中起什么作用?

    我正在使用SLURM http slurm schedmd com 使用一些计算集群 它有 ntasks or n 我显然已经阅读了它的文档 http slurm schedmd com sbatch html http slurm sch
  • 为什么清空 NavigationStack 路径后对象仍在内存中?

    我正在尝试实现一个协调器来管理流程 状态存储在 CoordinatorStore 内 有 2 个用于管理流程的 Published 属性 这screen属性控制当前显示哪个视图以及path控制堆栈视图的导航堆栈 实施细节可以在下面找到 根据