簡體   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