【问题标题】:Access @StateObject from function outside of view从视图外的函数访问@StateObject
【发布时间】:2021-11-06 03:45:28
【问题描述】:

我有一个符合“ObservableObject”协议的类(WatchlistClass),它拥有一个@Published var(监视列表)。反过来,var 包含一系列股票和一些信息([StockInformation]),它应该用当前的股票价格和东西更新我的视图(WatchlistView)。该部分工作得很好,但是该类应该访问视图中的@StateObject 以更改数据。直接在类中访问它是行不通的,因为它不会从@StateObject 读取数据。我尝试直接访问视图中的@StateObject,但这也创建了一个具有空数组的类的新实例。在 @Published var 上使用“静态”会引发错误。在我的一生中,我无法弄清楚如何直接在视图内访问 @StateObject 以读取和修改它所拥有的数据。任何帮助将不胜感激。

class WatchlistClass: ObservableObject {
    static let shared = WatchlistClass()
    
    @Published var watchlist: [StockInformation] = []
    
    struct StockInformation: Identifiable {
        ...
    }
    

    func WatchlistTasksGetFundamentalsDelegate(string: String) {
        ...            
            DispatchQueue.main.async {
                self.watchlist.append(stockInformation) // This works and updates the view as expected
            }
        ...
    }


    private let timer = Timer.scheduledTimer(withTimeInterval: 4.0, repeats: true) { _ in
        if self.watchlist.isEmpty == false { // This does not work
            ...
    }
}
struct WatchlistView: View {
    @StateObject var watchlistClass = WatchlistClass()
    ...
}

【问题讨论】:

  • 这些答案是否回答了您的问题或对您有帮助?
  • 很抱歉这么晚才回答,过去几天我没有 XCode 来尝试答案。

标签: swift swiftui property-wrapper


【解决方案1】:

您的timer 是一个实例变量,但它的闭包不是该类的实例并且没有self

您将不得不做一些事情来将“自我”放入计时器块的范围内。一种方法是在实例的成员函数中创建计时器:

private func makeTimer() -> Timer {
    return Timer.scheduledTimer(withTimeInterval: 4.0, repeats: true) { [weak self] _ in
        if let empty = self?.watchlist.isEmpty,
            empty == false {
        }
    }
}

当您调用 makeTimer() 时,您的代码将在实例的上下文中执行。它将可以访问self

请注意,我已更改您的块,以便它捕获 self“弱”,因为计时器可能存在于对象的生命周期之外,因此您必须积极应对这种可能性。

您可以从初始化程序中调用 makeTimer。

【讨论】:

  • 我已经实施了您的解决方案,它运行良好,非常感谢您。但是,我不明白为什么闭包不是类的实例?我尝试在网上搜索,但我真的找不到任何资源,所以我想问你有没有?谢谢。
  • 闭包在类的“词法范围”内,但它是函数scheduledTimer(withTimeInterval:repeats)的参数,与类本身没有关联。生成的计时器存储为类的实例变量,但闭包本身不是类的一部分。
【解决方案2】:

使用单例模式时,您通常希望将对象上的init 声明为私有,这样您就可以确定只能创建它的一个实例。

然后,要访问该单例版本,您将使用 WatchlistClass.shared

class WatchlistClass: ObservableObject {
    static let shared = WatchlistClass()
    
    @Published var watchlist: [StockInformation] = []
    
    struct StockInformation: Identifiable {
        //...
    }
    
    private init() { }
}

struct WatchlistView: View {
    @StateObject var watchlistClass = WatchlistClass.shared
    
    var body: some View {
        //...
    }
}

就您的Timer 而言,了解更多关于它在此处服务的目的的信息会很有帮助。例如,如果它只是检查watchlist 对其做出反应,我的建议是使用Combine 来观看它而不是Timer

如果单例版本的目的只是尝试访问 Timer 并让它能够访问数组,那么这样的模式可能会更好:

import Combine

class WatchlistClass: ObservableObject {
    
    @Published var watchlist: [StockInformation] = []
    
    private var cancellable : AnyCancellable?
    
    struct StockInformation: Identifiable {
        var id = UUID()
        //...
    }
    
    public init() {
        cancellable = Timer.publish(every: 1, on: .main, in: .default)
            .autoconnect()
            .receive(on: RunLoop.main)
            .sink { _ in
                if !self.watchlist.isEmpty {
                    
                }
            }
    }
}

struct WatchlistView: View {
    @StateObject var watchlistClass = WatchlistClass()
    
    var body: some View {
        Text("Hello, world!")
    }
}

这将在init 上创建Timer,并授予它访问实例的watchlist 数组的权限。

【讨论】:

  • 单例是我自己解决问题的一种尝试,但并不是真的需要,因为我只初始化了一次类。 “计时器”每 4 秒下载一次股市数据,并遍历“观察列表”中的每个对象以更新其价格和数量,这就是我必须访问“观察列表”实例的原因。我曾尝试通过 watchlistClass.shared 访问它,但没有成功,我认为因为它不是该类的实例,所以它无法访问 self。 Scott Thompson 提供的答案解决了我的问题,但感谢您与我联系。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-07-14
  • 2022-07-19
  • 1970-01-01
  • 1970-01-01
  • 2012-03-06
  • 1970-01-01
  • 2017-05-25
相关资源
最近更新 更多