简体   繁体   English

Firebase Swift 3数据库在具有CompletionBlock的setValue上崩溃

[英]Firebase Swift 3 Database crashes on setValue withCompletionBlock

I am using Firebase on iOS with Swift 3. 我在iOS上使用Swift 3的Firebase。

When I use 当我使用

FIRDatabase.database().reference().child("child").setValue("value") { 
  (error: Error?, databaseReference: FIRDatabaseReference) in
    print("Error while setting value \(error)")
}   

The app crashes on runtime with the following log: 该应用程序在运行时因以下日志而崩溃:

*** Terminating app due to uncaught exception 'InvalidFirebaseData', reason: '(nodeFrom:priority:) Cannot store object of type _SwiftValue at . ***由于未捕获的异常'InvalidFirebaseData'而终止应用程序,原因:'(nodeFrom:priority :)无法将_SwiftValue类型的对象存储在。 Can only store objects of type NSNumber, NSString, NSDictionary, and NSArray.' 只能存储NSNumber,NSString,NSDictionary和NSArray类型的对象。

I tried to use the same function but without the trailing closure and for some reason, it works! 我尝试使用相同的功能,但没有尾随的闭包,并且由于某种原因,它起作用了!

FIRDatabase.database().reference().child("child").setValue("value", 
  withCompletionBlock: { 
    (error: Error?, databaseReference: FIRDatabaseReference) in
      print("Error while setting value \(error)")
})

Is there something special about trailing closures and Swift 3? 尾随闭包和Swift 3有什么特别之处吗?

tl;dr: Firebase provides a setValue(_ value: Any?, andPriority priority: Any?) which is incorrectly matched when using a trailing closure with setValue(_ value: Any?, withCompletionBlock: (Error?, FIRDatabaseReference) -> Void) . 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)

Solution : When using an API that has many varieties, avoid using trailing closures. 解决方案 :使用种类繁多的API时,请避免使用尾随闭包。 In this case, prefer setValue(myValue, withCompletionBlock: { (error, dbref) in /* ... */ }) ; 在这种情况下,最好使用setValue(myValue, withCompletionBlock: { (error, dbref) in /* ... */ }) do not use setValue(myValue) { (error, dbref) in /* ... */ } . 使用setValue(myValue) { (error, dbref) in /* ... */ }

Explanation 说明

This appears to be a Swift bug. 这似乎是一个Swift错误。 As in other languages, such as Java, Swift generally chooses the most specific overload. 与其他语言(例如Java)一样,Swift通常选择最具体的重载。 Eg, 例如,

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

However, when overloaded functions match a trailing closure, Swift (at least, sometimes) selects the more general type. 但是,当重载函数与尾随闭包匹配时,Swift(至少有时是)会选择更通用的类型。 Eg, 例如,

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? is a more general type than () -> Void -- ie, "anything, even null" is more broad than "a function receiving 0 parameters and returning something of type Void ". 是比() -> Void更通用的类型-即,“任何东西,甚至为null”比“接收0参数并返回Void类型的函数”更广泛。 However, the trailing closure matches Any? 但是,结尾的闭包匹配Any? ; ; this is the opposite of what you would expect from a language that matches the most specific type. 这与您希望与最特定的类型匹配的语言相反。

While there is an accepted answer, which provides some clarity, explaining that it's a Swift bug is not really accurate. 尽管有一个可以接受的答案(提供了一些明确的答案),但说明这是一个Swift错误并不十分准确。 That being said, the explanation is accurate but not for this issue. 话虽如此,但解释是准确的,但不适用于此问题。

Allowing the closure to be added to setValue in the first place is the real bug. 真正的错误是允许首先将闭包添加到setValue中。

A more accurate answer is that there is no completion block/closure for the setValue function , which is why it fails. 一个更准确的答案是setValue函数没有完成块/关闭 ,这就是它失败的原因。

The specific function -setValue: does NOT have a closure, which is why it's crashing. 特定功能-setValue:没有闭包,这就是崩溃的原因。 ie it's an incorrect implementation in your code. 也就是说,这是您代码中的错误实现。 Per the docs: 根据文档:

func setValue(_ value: Any?)

note that the setValue function does NOT have a closure and if you add one the function will have no idea what to do with that data. 请注意,setValue函数没有闭包,如果添加一个闭包,该函数将不知道如何处理该数据。

To use a completion block/closure, you must call the correct function which is 要使用完成块/结束符,您必须调用正确的函数

-setValue:withCompletionBlock:

Bottom line is you can't randomly add a parameter or call to a function that's not designed to accept it. 最重要的是,您不能随机添加参数或调用未设计为接受该参数的函数。

This is obviously not valid but conceptually it's the same error. 这显然是无效的,但在概念上却是相同的错误。

    let a = "myString"
    a.append("x") { (error: Error?) }

In this case the compiler knows the the string.append function doesn't have a closure option and catches it before compiling. 在这种情况下,编译器知道string.append函数没有闭包选项,并在编译之前将其捕获。

So go a tad further, this code complies & runs but also generates an error 因此,进一步介绍一下,此代码符合并运行,但也会产生错误

ref.child("child").setValue("value") { }

Again, setValue doesn't have a closure so this code is improperly implemented. 同样,setValue没有闭包,因此该代码未正确实现。

To clarify, given a class 为了澄清,给定一个类

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
    }
}

Note that setValue:aString is a totally different function than setValue:aString:someCompletionHandler 请注意,setValue:aString是与setValue:aString:someCompletionHandler完全不同的函数

The only parameter that can be based to setValue:aString is a String as the first and only parameter. 可以基于setValue:aString的唯一参数是字符串,这是第一个也是唯一的参数。

The setValue:aString:someCompletionHandler will be passed two parameters, a String in the first position and a completionHandler in the second position. setValue:aString:someCompletionHandler将被传递两个参数,第一个位置为String,第二个位置为completionHandler。

The actual completion block is the second parameter passed. 实际完成块是传递的第二个参数。

param1, param2 ------, --------------- string, completionBlock param1,param2 ------,---------------字符串,completionBlock

This is why 这就是为什么

setValue(value) {}

is improperly formatted whereas 格式不正确,而

setValue(value, withBlock: {})

is properly formatted. 格式正确。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM