繁体   English   中英

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

[英]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.

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