【问题标题】:Accessing Array Outside Closure in Swift 3在 Swift 3 中访问闭包外的数组
【发布时间】:2017-02-03 02:50:38
【问题描述】:

我有这个闭包,我用它来填充我的数组和字典。但是,当我尝试在函数之外使用它时,它是空的。我知道这个闭包在异步线程上工作,所以假设我在填充之前尝试访问该变量是否正确?结果,我得到一个空数组。这是我的代码。

class HomeCollectionViewController: UICollectionViewController, UICollectionViewDelegateFlowLayout, UISearchBarDelegate, UIGestureRecognizerDelegate {

var entries = [String: DiaryEntry]()
var entryIDS =  [String]()

var searchController: UISearchController!

override func viewDidLoad() {
    super.viewDidLoad()

    // Register cell classes
    self.collectionView!.register(DiaryCell.self, forCellWithReuseIdentifier: "homeCell")
    collectionView?.backgroundColor = UIColor.white
    navigationController?.hidesBarsOnSwipe = true

    if let userID = FIRAuth.auth()?.currentUser?.uid {
        FirebaseService.service.getUserEntriesRef(uid: userID).observe(.value, with: { [weak weakSelf = self] (snapshot) in
            let enumerator = snapshot.children
            while let entry = enumerator.nextObject() as? FIRDataSnapshot {
                weakSelf?.entryIDS.append(entry.key)
                weakSelf?.entries[entry.key] = DiaryEntry(snapshot: entry)
            }
            weakSelf?.entryIDS.reverse()
            weakSelf?.collectionView?.reloadData()
        })
        print("Entries: \(entryIDS.count) ")
    }
    // Do any additional setup after loading the view.
}

处理这种多线程执行的最佳方法是什么?

【问题讨论】:

    标签: ios swift multithreading firebase closures


    【解决方案1】:

    使用Dispatch Groups 跟踪您完成将所有元素附加到数组的时间,然后进行notify 回调,当它们全部添加时会自动调用。

    class HomeCollectionViewController: UICollectionViewController, UICollectionViewDelegateFlowLayout, UISearchBarDelegate, UIGestureRecognizerDelegate {
    
    var entries = [String: DiaryEntry]()
    var entryIDS =  [String]()
    
    let dispatchGroup = DispatchGroup()
    
    var searchController: UISearchController!
    
    override func viewDidLoad() {
        super.viewDidLoad()
    
        // Register cell classes
        self.collectionView!.register(DiaryCell.self, forCellWithReuseIdentifier: "homeCell")
        collectionView?.backgroundColor = UIColor.white
        navigationController?.hidesBarsOnSwipe = true
        self.dispatchGroup.enter()
        if let userID = FIRAuth.auth()?.currentUser?.uid {
            FirebaseService.service.getUserEntriesRef(uid: userID).observe(.value, with: { [weak weakSelf = self] (snapshot) in
                let enumerator = snapshot.children
                while let entry = enumerator.nextObject() as? FIRDataSnapshot {
                    weakSelf?.entryIDS.append(entry.key)
                    weakSelf?.entries[entry.key] = DiaryEntry(snapshot: entry)
                }
                self.dispatchGroup.leave()
            })
            self.dispatchGroup.notify(queue: DispatchQueue.main, execute: {
                 print("Entries: \(entryIDS.count) ")
                 weakSelf?.entryIDS.reverse()
                 weakSelf?.collectionView?.reloadData()
            })
        }
        // Do any additional setup after loading the view.
    }
    

    我还建议只使用self 而不是weakSelf?

    【讨论】:

    • 如果我想在一个名为 menuButtonPressed() 的方法中访问这个数组和字典,这会起作用吗?我在这里想要实现的是,当用户点击屏幕上的 menuImage 时,我会调用这个 menuButtonPressed() 方法,并且我想将该数组和字典访问到这个方法中。
    • 一旦您的 Firebase 提取发生,您的数组将被填充,因此它应该可以工作。调度组的好处是它们不会阻塞主线程。
    • 很抱歉出现分歧,但我仍然不知道 DispatchGroup 将如何帮助我解决问题。就像闭包一样,它不会阻塞主线程,我最终会在这些变量可用或填充之前访问它们。我说的是通过某种方法访问它们。当您尝试打印(“Entries:(entryIDS.count)”)时,上面的代码将起作用。
    • 也许我不明白你的情况。如果您想在用户按下按钮后从数据库中获取某些内容,然后在获取该数据后填充 collectionView,这就是执行此操作的方法。一旦从您的数据库中检索到数据,notify 回调将使用填充的数组重新加载 collectionView。
    • 对不起,我应该更好地解释我的问题。让我再试一次:获取 firebase 数据工作正常。当我的收藏视图加载时,每个单元格都有这个菜单按钮。当他们单击任何单元格上的该菜单按钮时,我想使用该数组和字典执行操作。不幸的是,他们都空了。所以我的猜测是我的方法没有得到该闭包填充的数据。我希望这可以帮助您理解我的问题。
    【解决方案2】:

    我遵循 Raywenderlich 及其团队的编码标准(针对 Swift)。如果我要重写你的代码来拥有一个强大的自我,它会是这样的:

    class HomeCollectionViewController: UICollectionViewController, UICollectionViewDelegateFlowLayout, UISearchBarDelegate, UIGestureRecognizerDelegate {
    
        var entries = [String: DiaryEntry]()
        var entryIDS =  [String]()
    
        var searchController: UISearchController!
    
        override func viewDidLoad() {
            super.viewDidLoad()
    
            // Register cell classes
            self.collectionView!.register(DiaryCell.self, forCellWithReuseIdentifier: "homeCell")
            collectionView?.backgroundColor = UIColor.white
            navigationController?.hidesBarsOnSwipe = true
    
            if let userID = FIRAuth.auth()?.currentUser?.uid {
                FirebaseService.service.getUserEntriesRef(uid: userID).observe(.value, with: {
                    [weak self] (snapshot) in
    
                    guard let strongSelf = self else {
                        return
                    }
    
                    let enumerator = snapshot.children
                    while let entry = enumerator.nextObject() as? FIRDataSnapshot {
                        strongSelf.entryIDS.append(entry.key)
                        strongSelf.entries[entry.key] = DiaryEntry(snapshot: entry)
                    }
                    strongSelf.entryIDS.reverse()
                    strongSelf.collectionView?.reloadData()
                })
                print("Entries: \(entryIDS.count) ")
            }
            // Do any additional setup after loading the view.
    }
    

    我希望这有效。在连接到任何 API(例如 Firebase 和 Alamofire)时,我也在编写这样的代码。

    【讨论】:

    • 我会试试的,但你认为强大的自我是这里的问题吗?
    • 可能。我记得当我刚接触 Swift 时,我也无法在闭包内访问我的类中的属性。
    • 但这不等于说weak weakSelf = self.我在斯坦福大学的一门 iOS 课程中看到了这一点。我可能是错的。
    • 是的,我测试了你在闭包内处理 self 的方式,它奏效了。回到您的问题,您甚至可以在填充数据源之前使用和访问您的数据源,因为您已经对其进行了初始化。我敢打赌,您在获取数据时出错了?或处理快照。两者中的任何一个。你可以使用断点做更多的测试和打印对象吗?
    • 我可以使用它们,但它们是空的。然而,你是对的。让我做一些调试...
    【解决方案3】:

    我刚刚遇到了您的问题,同时寻找相同问题的解决方案,我终于设法弄清楚了。所以,我会尽量回答这对我们后面的许多人会有所帮助。

    问题是数组只存在于闭包内部。因此,解决方案是在 viewDidLoad 之外创建一个数组,并在拥有完整数组后使用它进行设置,然后使用 didSet 设置 entryIDS

    var entryIDS =  [String]() {
        didSet {
            //something
         }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-07-18
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多