ios - realtime - swift queryordered




Firebase Swift 3 setValue withCompletionBlockでデータベースがクラッシュする (2)

tl; dr:Firebaseは、 setValue(_ value: Any?, andPriority priority: Any?) setValue(_ value: Any?, withCompletionBlock: (Error?, FIRDatabaseReference) -> Void)を使用して後続のクロージャを使用すると、誤って一致するsetValue(_ value: Any?, withCompletionBlock: (Error?, FIRDatabaseReference) -> Void)

解決策 :さまざまな種類のAPIを使用する場合は、後続のクロージャを使用しないでください。 この場合、 setValue(myValue, withCompletionBlock: { (error, dbref) in /* ... */ })ます。 setValue(myValue) { (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よりも一般的な型です。つまり、「何か、偶数も」は、「0のパラメータを受け取り、 Void型のものを返す関数」よりも広いです。 ただし、末尾のクロージャはAny?一致しAny? ; これは、最も具体的なタイプに一致する言語から期待されるものとは逆です。

Swift 3でiOS上で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について特別なことはありますか?


受け入れられた答えがありますが、それは明快さを提供しますが、スウィフトバグであることを説明することは本当に正確ではありません。 つまり、説明は正確ですが、この問題ではありません。

クロージャが最初に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は、最初の唯一のパラメータとしてのStringです。

setValue:aString:someCompletionHandlerは2つのパラメータ、つまり最初の位置のStringと2番目の位置のcompletionHandlerを渡します。

実際の完了ブロックは、渡された2番目のパラメータです。

param1、param2 ------、---------------文字列、completionBlock

これが理由です

setValue(value) {}

不適切な形式になっています

setValue(value, withBlock: {})

適切にフォーマットされています。







firebase-database