![](/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.