gpt4 book ai didi

ios - 从函数返回之前等待 Firebase 加载

转载 作者:行者123 更新时间:2023-11-29 05:31:59 25 4
gpt4 key购买 nike

我有一个从 Firebase 加载数据的简单函数。

func loadFromFireBase() -> Array<Song>? {
var songArray:Array<Song> = []

ref.observe(.value, with: { snapshot in
//Load songArray
})

if songArray.isEmpty {
return nil
}
return songArray
}

目前,即使有数据要加载,该函数也始终返回nil。它这样做是因为它永远不会到达执行完成 block ,在函数返回之前它会加载数组。我正在寻找一种方法,使函数仅在调用完成 block 后返回,但我无法将 return 放入完成 block 中。

最佳答案

(这个问题不断出现变化。我永远找不到一个好的、全面的答案,所以下面尝试提供这样的答案)

你不能这么做。 Firebase 是异步的。它的函数接受一个完成处理程序并立即返回。您需要重写 loadFromFirebase 函数以获取完成处理程序。

我在 Github 上有一个名为 Async_demo 的示例项目(链接)是一个演示此技术的工作(Swift 3)应用程序。

其中的关键部分是函数 downloadFileAtURL,它采用完成处理程序并执行异步下载:

typealias DataClosure = (Data?, Error?) -> Void

/**
This class is a trivial example of a class that handles async processing. It offers a single function, `downloadFileAtURL()`
*/
class DownloadManager: NSObject {

static var downloadManager = DownloadManager()

private lazy var session: URLSession = {
return URLSession.shared
}()

/**
This function demonstrates handling an async task.
- Parameter url The url to download
- Parameter completion: A completion handler to execute once the download is finished
*/

func downloadFileAtURL(_ url: URL, completion: @escaping DataClosure) {

//We create a URLRequest that does not allow caching so you can see the download take place
let request = URLRequest(url: url,
cachePolicy: .reloadIgnoringLocalAndRemoteCacheData,
timeoutInterval: 30.0)
let dataTask = URLSession.shared.dataTask(with: request) {
//------------------------------------------
//This is the completion handler, which runs LATER,
//after downloadFileAtURL has returned.
data, response, error in

//Perform the completion handler on the main thread
DispatchQueue.main.async() {
//Call the copmletion handler that was passed to us
completion(data, error)
}
//------------------------------------------
}
dataTask.resume()

//When we get here the data task will NOT have completed yet!
}
}

上面的代码使用 Apple 的 URLSession 类从远程服务器异步下载数据。创建 dataTask 时,您传入一个完成处理程序,当数据任务完成(或失败)时,该处理程序将被调用。但请注意:您的完成处理程序是在后台线程上调用的。

这很好,因为如果您需要执行耗时的处理(例如解析大型 JSON 或 XML 结构),您可以在完成处理程序中执行此操作,而不会导致应用程序的 UI 卡住。但是,因此,如果不将这些 UI 调用发送到主线程,则无法在数据任务完成处理程序中执行 UI 调用。上面的代码使用 DispatchQueue.main.async() {} 调用来调用主线程上的整个完成处理程序。

返回OP的代码:

我发现以闭包作为参数的函数很难阅读,因此我通常将闭包定义为类型别名。

重新编写@Raghav7890答案中的代码以使用类型别名:

typealias SongArrayClosure = (Array<Song>?) -> Void

func loadFromFireBase(completionHandler: @escaping SongArrayClosure) {
ref.observe(.value, with: { snapshot in
var songArray:Array<Song> = []
//Put code here to load songArray from the FireBase returned data

if songArray.isEmpty {
completionHandler(nil)
}else {
completionHandler(songArray)
}
})
}

我已经很长时间没有使用 Firebase了(然后只修改了别人的 Firebase 项目),所以我不记得它是在主线程还是在后台线程上调用它的完成处理程序。如果它在后台线程上调用完成处理程序,那么您可能需要将对完成处理程序的调用包装在对主线程的 GCD 调用中。


编辑:

基于this SO question的答案,听起来 Firebase 在后台线程上进行网络调用,但在主线程上调用它的监听器。

在这种情况下,您可以忽略下面的 Firebase 代码,但对于那些阅读本线程以获取其他类型异步代码帮助的人,您可以按照以下方式重写代码以在主线程上调用完成处理程序:

typealias SongArrayClosure = (Array<Song>?) -> Void

func loadFromFireBase(completionHandler:@escaping SongArrayClosure) {
ref.observe(.value, with: { snapshot in
var songArray:Array<Song> = []
//Put code here to load songArray from the FireBase returned data

//Pass songArray to the completion handler on the main thread.
DispatchQueue.main.async() {
if songArray.isEmpty {
completionHandler(nil)
}else {
completionHandler(songArray)
}
}
})
}

关于ios - 从函数返回之前等待 Firebase 加载,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57451817/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com