我从火力地堡一个简单的函数加载数据。
func loadFromFireBase() -> Array<Song>? {
var songArray:Array<Song> = []
ref.observe(.value, with: { snapshot in
//Load songArray
})
if songArray.isEmpty {
return nil
}
return songArray
}
目前该函数返回nil
始终,即使有数据加载。它这样做是因为它没有以往任何时候都得到它在函数返回之前加载阵列的执行完成块。我正在寻找一种方法,使该函数只返回一旦完成块已被调用,但是我不能把在完成座返回。
(在这个问题上的变化上来不断地对SO,我永远无法找到一个良好的,全面的答案,所以下面是一个尝试提供这样一个答案)
你不能做到这一点。火力地堡是异步的。其功能需要完成处理并立即返回。你需要重写你的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!
}
}
上面的代码使用苹果的URLSession
类异步从远程服务器下载数据。当你创建一个dataTask
,你在当数据任务已完成了被调用完成处理程序通过当心,虽然(或失败):您完成处理程序被在后台线程调用。
这是很好的,因为如果你需要做费时的处理一样大解析JSON或XML结构,你可以在完成处理程序去做,而不会导致您的应用程序的UI冻结。但是,作为一个结果,你不能这样做UI数据任务完成处理,而不发送这些UI调用主线程调用。上面的代码调用主线程上的整个完成处理程序,使用呼叫DispatchQueue.main.async() {}
。
回到OP的代码:
我发现,与闭合作为参数的函数是难以阅读,所以我通常定义的闭包作为typealias。
从@ Raghav7890的答案返工的代码使用typealias:
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)
}
})
}
我没有在很长一段时间使用火力地堡(然后只修改别人的火力地堡项目),所以我不记得是否调用它是在主线程或后台线程完成处理。如果调用在后台线程完成处理,那么你可能想包装调用您的完成处理器在GCD调用主线程。
基于答案this SO question,这听起来像火力地堡做它的网络在后台线程调用,但调用它的主线程上的听众。
在这种情况下,你可以忽略下面的火力地堡的代码,但对于那些阅读此线程与其他种类的异步代码的帮助,这里是你将如何重写代码来调用主线程中完成处理:
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)
}
}
})
}
使邓肯回答更精确。你可以作出这样的功能
func loadFromFireBase(completionHandler:@escaping (_ songArray: [Song]?)->()) {
ref.observe(.value) { snapshot in
var songArray: [Song] = []
//Load songArray
if songArray.isEmpty {
completionHandler(nil)
}else {
completionHandler(songArray)
}
}
}
您可以在完成处理程序块返回songArray。