2

我正在使用 HanekeSwift 检索缓存的数据,然后在每次视图出现时将其设置为 swipeView 中的标签。我的代码检索数据没有问题,但是因为cache.fetch()是异步的,所以当我调用我的方法来更新视图时,我的标签设置为nil. 无论如何要告诉 swift 在加载视图之前等到我的缓存数据被检索到?

请参见下面的代码:

override func viewWillAppear(animated: Bool) {
    updateEntries() // updates entries from cache when view appears
}

func updateEntries() {
    guard let accessToken = NSUserDefaults.standardUserDefaults().valueForKey("accessToken") as? String else { return }
    guard let cachedEntryKey = String(accessToken) + "food_entries.get" as? String else { return }
    cache.fetch(key: cachedEntryKey).onSuccess { data in
        ...
        // if successful, set labels in swipeView to data retrieved from cache
        ...
        dispatch_group_leave(dispatchGroup)
    } .onFailure { error in
        print(error)
        ...
        // if unsuccessful, call servers to retrieve data, set labels in swipeView to that data
        ...
        dispatch_group_leave(dispatchGroup)
    }
}

当我单步执行上述代码时,它总是显示视图,然后进入缓存块。如何使 viewWillAppear() 允许 updateEntries() 完成并且在执行缓存块之前不会退出它?提前致谢!

更新1:

下面的解决方案运行良好,我的调用以正确的顺序进行(我在通知块中的打印语句在缓存检索后执行),但我的视图仅在调用服务器时使用非零值更新它们的标签。也许我在通知组中混入了错误的代码?

override func viewWillAppear(animated: Bool) {
    self.addProgressHUD()
    updateEntries() // updates entries from cache when view appears
}

func updateEntries() {
    guard let accessToken = NSUserDefaults.standardUserDefaults().valueForKey("accessToken") as? String else { return }
    guard let cachedEntryKey = String(accessToken) + "food_entries.get" as? String else { return }

    let dispatchGroup = dispatch_group_create()
    dispatch_group_enter(dispatchGroup)

    dispatch_group_async(dispatchGroup, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0)) {
        cache.fetch(key: cachedEntryKey).onSuccess { data in
            ...
            // if successful, set labels in swipeView to data retrieved from cache
            ...
        } .onFailure { error in
            print(error)
            ...
            // if unsuccessful, call servers to retrieve data, set labels in swipeView to that data
            ...
        }
    }

    dispatch_group_notify(dispatchGroup, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0)) {
        print("Retrieved Data")
        self.removeProgressHUD()
    }

}

更新 2:

此外,当我切换视图时,我会在控制台中收到此警告。我想我用上面的代码锁定了主线程

“此应用程序正在从后台线程修改自动布局引擎,这可能导致引擎损坏和奇怪的崩溃。这将在未来的版本中导致异常。”

4

4 回答 4

1

每个人的好建议对此都有很大帮助。想我明白了。我需要确保我的缓存块没有阻塞主队列。请参阅下面的代码

编辑

感谢@Rob 帮助我进行适当的调整以完成这项工作

let dispatchGroup = dispatch_group_create()
dispatch_group_enter(dispatchGroup)

cache.fetch(key: cachedEntryKey).onSuccess { data in
    ...
    // if successful, set labels in swipeView to data retrieved from cache
    ...
    dispatch_group_leave(dispatchGroup)
} .onFailure { error in
    print(error)
    ...
    // if unsuccessful, call servers to retrieve data, set labels in swipeView to that data
    ...
    dispatch_group_leave(dispatchGroup)
}

dispatch_group_notify(dispatchGroup, dispatch_get_main_queue()) {
    print("Retrieved Data")
    self.removeProgressHUD()
}
于 2016-09-05T14:53:42.620 回答
1

笔记:

  • 在调用异步方法之前进入组
  • 离开组是每个各自的完成/失败处理程序
  • 将通知块中的 UI 更新分派到队列

因此:

func updateEntries() {
    guard let accessToken = NSUserDefaults.standardUserDefaults().valueForKey("accessToken") as? String else { return }
    guard let cachedEntryKey = String(accessToken) + "food_entries.get" as? String else { return }

    let group = dispatch_group_create()
    dispatch_group_enter(group)

    cache.fetch(key: cachedEntryKey).onSuccess { data in
        ...
        // if successful, set labels in swipeView to data retrieved from cache
        ...
        dispatch_group_leave(group)
    } .onFailure { error in
        print(error)
        ...
        // if unsuccessful, call servers to retrieve data, set labels in swipeView to that data
        ...
        dispatch_group_leave(group)
    }

    dispatch_group_notify(group, dispatch_get_main_queue()) {
        print("Retrieved Data")
        self.removeProgressHUD()
    }

}
于 2016-09-05T16:40:59.407 回答
0

这是一个简单的示例,您可以暂存加载屏幕。我只是创建一个警报视图,您也可以创建自定义加载指示器视图

let alert = UIAlertController(title: "", message: "please wait ...", preferredStyle: .alert)

override func viewWillAppear(animated: Bool) {
    self.present(alert, animated: true, completion: nil)
    updateEntries() // updates entries from cache when view appears
}

func updateEntries() {
    guard let accessToken = UserDefaults.standard.value(forKey: "accessToken") as? String,
          let cachedEntryKey = (accessToken + "food_entries.get") as? String else { 
      return 
    }
    cache.fetch(key: cachedEntryKey).onSuccess { data in
        ...
             // update value in your UI
             alert.dismiss(animated: true, completion: nil)
        ...
        } .onFailure { error in
            print(error)
            ...
                // if unsuccessful, call servers to retrieve data, set labels in swipeView to that data
            ...
    }
}
于 2016-09-05T03:45:19.503 回答
0

虽然我完全同意@ozgur 从用户体验的角度显示某种加载指示器,但我认为学习如何使用Grand Central Dispatch(Apple 的异步等待本地解决方案)的好处可能会长期帮助您。


dispatch_groups在运行某种完成处理程序之前,您可以使用等待代码块完全完成运行。

来自苹果的文档

调度组是一种监视一组块的机制。您的应用程序可以根据您的需要同步或异步监视组中的块。通过扩展,组可用于同步依赖于其他任务完成的代码。

[...]

调度组跟踪有多少块未完成,GCD 保留该组,直到其所有关联的块完成执行。

这是一个实际操作的示例dispatch_groups

let dispatchGroup = dispatch_group_create()

dispatch_group_async(dispatchGroup, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0)) {
    // Run whatever code you need to in here. It will only move to the final
    // dispatch_group_notify block once it reaches the end of the block.
}

dispatch_group_notify(dispatchGroup, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0)) {
    // Code in here only runs once all dispatch_group_async blocks associated
    // with the dispatchGroup have finished completely.
}

最重要的dispatch_groups是,它们允许您同时运行多个异步块并等待它们全部完成,然后再运行最终的完成处理程序。换句话说,您可以根据需要将任意数量的dispatch_group_async块与关联dispatchGroup

如果您想采用加载指示器方法(您应该这样做),您可以运行代码以显示加载指示器,然后dispatch_group使用完成处理程序进入 a 以删除加载指示器并在dispatch_group完成后将数据加载到视图中。

于 2016-09-05T04:08:27.563 回答