![](/img/trans.png)
[英]Firebase withCompletionBlock not called if there is no connection
[英]Firebase Swift 3 Database crashes on setValue withCompletionBlock
我在iOS上使用Swift 3的Firebase。
當我使用
FIRDatabase.database().reference().child("child").setValue("value") {
(error: Error?, databaseReference: FIRDatabaseReference) in
print("Error while setting value \(error)")
}
該應用程序在運行時因以下日志而崩潰:
***由於未捕獲的異常'InvalidFirebaseData'而終止應用程序,原因:'(nodeFrom:priority :)無法將_SwiftValue類型的對象存儲在。 只能存儲NSNumber,NSString,NSDictionary和NSArray類型的對象。
我嘗試使用相同的功能,但沒有尾隨的閉包,並且由於某種原因,它起作用了!
FIRDatabase.database().reference().child("child").setValue("value",
withCompletionBlock: {
(error: Error?, databaseReference: FIRDatabaseReference) in
print("Error while setting value \(error)")
})
尾隨閉包和Swift 3有什么特別之處嗎?
tl; dr:Firebase提供了setValue(_ value: Any?, andPriority priority: Any?)
,該值在使用帶有setValue(_ value: Any?, withCompletionBlock: (Error?, FIRDatabaseReference) -> Void)
setValue(_ value: Any?, andPriority priority: Any?)
的尾隨閉包時不正確匹配setValue(_ value: Any?, withCompletionBlock: (Error?, FIRDatabaseReference) -> Void)
。
解決方案 :使用種類繁多的API時,請避免使用尾隨閉包。 在這種情況下,最好使用setValue(myValue, withCompletionBlock: { (error, dbref) in /* ... */ })
; 不使用setValue(myValue) { (error, dbref) in /* ... */ }
。
說明
這似乎是一個Swift錯誤。 與其他語言(例如Java)一樣,Swift通常選擇最具體的重載。 例如,
class Alpha {}
class Beta : Alpha {}
class Charlie {
func charlie(a: Alpha) {
print("\(#function)Alpha")
}
func charlie(a: Beta) {
print("\(#function)Beta")
}
}
Charlie().charlie(a: Alpha()) // outputs: charlie(a:)Alpha
Charlie().charlie(a: Beta() as Alpha) // outputs: charlie(a:)Alpha
Charlie().charlie(a: Beta()) // outputs: charlie(a:)Beta
但是,當重載函數與尾隨閉包匹配時,Swift(至少有時是)會選擇更通用的類型。 例如,
class Foo {
func foo(completion: () -> Void) {
print(#function)
}
func foo(any: Any?) {
print(#function)
}
}
func bar() {}
Foo().foo(completion: bar) // outputs: foo(completion:)
Foo().foo(any: bar) // outputs: foo(any:)
Foo().foo() { () in } // outputs: foo(any:)
// ^---- Here lies the problem
// Foo().foo(bar) will not compile; can't choose between overrides.
Any?
是比() -> Void
更通用的類型-即,“任何東西,甚至為null”比“接收0參數並返回Void
類型的函數”更廣泛。 但是,結尾的閉包匹配Any?
; 這與您希望與最特定的類型匹配的語言相反。
盡管有一個可以接受的答案(提供了一些明確的答案),但說明這是一個Swift錯誤並不十分准確。 話雖如此,但解釋是准確的,但不適用於此問題。
真正的錯誤是允許首先將閉包添加到setValue中。
一個更准確的答案是setValue函數沒有完成塊/關閉 ,這就是它失敗的原因。
特定功能-setValue:沒有閉包,這就是崩潰的原因。 也就是說,這是您代碼中的錯誤實現。 根據文檔:
func setValue(_ value: Any?)
請注意,setValue函數沒有閉包,如果添加一個閉包,該函數將不知道如何處理該數據。
要使用完成塊/結束符,您必須調用正確的函數
-setValue:withCompletionBlock:
最重要的是,您不能隨機添加參數或調用未設計為接受該參數的函數。
這顯然是無效的,但在概念上卻是相同的錯誤。
let a = "myString"
a.append("x") { (error: Error?) }
在這種情況下,編譯器知道string.append函數沒有閉包選項,並在編譯之前將其捕獲。
因此,進一步介紹一下,此代碼符合並運行,但也會產生錯誤
ref.child("child").setValue("value") { }
同樣,setValue沒有閉包,因此該代碼未正確實現。
為了澄清,給定一個類
class MyClass {
var s = ""
var compHandler = {}
func setValue(aString: String) {
self.s = aString
}
func setValue(aString: String, someCompletionHandler: completionHandler) {
self.s = aString
self.compHandler = someCompletionHandler
}
}
請注意,setValue:aString是與setValue:aString:someCompletionHandler完全不同的函數
可以基於setValue:aString的唯一參數是字符串,這是第一個也是唯一的參數。
setValue:aString:someCompletionHandler將被傳遞兩個參數,第一個位置為String,第二個位置為completionHandler。
實際完成塊是傳遞的第二個參數。
param1,param2 ------,---------------字符串,completionBlock
這就是為什么
setValue(value) {}
格式不正確,而
setValue(value, withBlock: {})
格式正確。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.