2017-09-08 13 views
1

私は、クロージャが汎用引数を受け取るオプションのクロージャ引数を使ってメソッドを呼び出す必要があるシナリオを持っています。私はここで何をしようとしている複雑な一般的な引数を持つスウィフトクロージャ

class FooModel 
{ 
} 

class FooSubClass1 : FooModel 
{ 
} 

class FooSubClass2 : FooModel 
{ 
} 

class Client 
{ 
    func httpGet<T:FooModel>(closure:((T) -> Void)? = nil) 
    { 
     // Doing some network request stuff here and call onHTTPRequestComplete() when done! 
     onHTTPRequestComplete(data: result, closure: closure) 
    } 

    func onHTTPRequestComplete<T:FooModel>(data:[String:Any], closure:((T) -> Void)? = nil) 
    { 
     if let c = closure 
     { 
      switch(T) 
      { 
       case is FooSubClass1: 
        var foo = FooSubClass1() 
        // Process data into new FooSubClass1 object here! 
        c(foo) 
       case is FooSubClass2: 
        var foo = FooSubClass2() 
        // Process data into new FooSubClass2 object here! 
        c(foo) 
      } 
     } 
    } 
} 


class App 
{ 
    func someFunc() 
    { 
     client.httpGet() 
     { 
      response in 
      print("\(response)") 
     } 
    } 
} 

: 私はスーパークラスといくつかのサブクラスを持つデータモデルを持ってここに私の簡略化されたコードです。 Client私はデータを取得するためにhttpリクエストを行い、取得されたデータでモデルオブジェクトを設定したいと思います。 Appの呼び出しメソッドは、非同期ネットワーク要求が完了し、正しいデータモデルオブジェクトを取得した後に呼び出されるクロージャを提供できる必要があります。

私のコードを持ついくつかの問題があります。

switch(T):エラー:予想されるメンバー名またはコンストラクタ呼び出しが

c(foo)タイプ名の後に:エラー: '(FooSubClass1) - >ボイドは' 'に変換できません(T) - > Void '

私の閉包の定義が私がしようとしていることに合っているかどうかはわかりません。

更新コード:

protocol FooModel 
{ 
    init() 
} 

class FooModelBase : FooModel 
{ 
} 

class FooSubClass1 : FooModelBase 
{ 
} 

class FooSubClass2 : FooModelBase 
{ 
} 

class Client 
{ 
    func httpGet<T:FooModel>(closure:((T) -> Void)? = nil) 
    { 
     // Doing some network request stuff here and call onHTTPRequestComplete() when done! 
     onHTTPRequestComplete(data: result, closure: closure) 
    } 

    func onHTTPRequestComplete<T:FooModel>(data:[String:Any], closure:((T) -> Void)? = nil) 
    { 
     if let c = closure 
     { 
      switch T.self 
      { 
       case is FooSubClass1.Type: 
        var foo = FooSubClass1() 
        // Assign retrieved values to model! 
        closure(foo) 
       case is FooSubClass2.Type: 
        var foo = FooSubClass2() 
        // Assign retrieved values to model! 
        closure(foo) 
       default: 
        break 
      } 
     } 
    } 
} 


class App 
{ 
    func someFunc() 
    { 
     client.httpGet() 
     { 
      response in 
      print("\(response)") // Should output FooSubClass1 instance with assigned values! 
     } 
    } 
} 

ほとんど動作しますが、私は以下のコンパイルエラーを取得:もちろんswitch(T)エラーのため

'closure(foo): Error: '(@lvalue FooSubClass1) -> Void' is not convertible to '(T) -> Void' 
+1

あなたのため、これはまだ動作しません。クロージャーは一般的なTを取る必要があります。代わりに '((FooModel) - > Void)? 'のようなものが必要です(この時点でジェネリックスを取り除くことができます)が、クロージャーでは適切にキャストする必要がありますそれは、そうです。 (これを解決するための多くの方法、つまり別のパラメータを渡して、それがどのような型かを知るなど) – TNguyen

+1

@BadmintonCatジェネリックパラメータの型を扱うswitch文を書くと、ジェネリックは_abstraction_です。 'T'を使用するメソッドは、メソッド内で定義した狭いリストだけでなく、' T'を取るこ​​とができなければなりません。私の意見では、すべてを考え直す。 –

+0

最小限のもの:https://pastebin.com/0WDWh1Cb –

答えて

1

あなたはこのエラーを取得します。タイプを切り替えようとしているが、値を切り替える必要があるからです。 2番目のエラーでは、Tが一般的であるためにクロージャーが期待していたものがあるため、完璧な意味合いがあります。閉鎖中ジェネリック医薬品に関する詳しい情報は、

このstackoverflowのスレッド

generics as parameters to a closure in swift

を見て、あなたがそこに何をしようとして、すべてのすべては、ちょうど不可能であり、一見、私はこれを信じます(私は文脈全体を見ることなく間違っているかもしれないが)入る正しい方向ではない。あなたの代わりに((Any) -> Void)?が閉鎖されていますが、残念ながらタイプはAnyであり、閉鎖の内側でスイッチを行う必要があります。

1

クロージャ内のTの値がクロージャ自体に渡されます。あなたはそれをオンにすることはできません。しかし、私が望むと思うのは、タイプを切り替えることです。しかし、this code smells

if FooSubClass1.self == T.self { 
    var foo = FooSubClass1(); 
} else if FooSubClass2.self == T.self { 
    var foo = FooSubClass2(); 
} 

let type = String(reflecting: T.self) 
switch (type) { 
case "Module.FooSubClass1": 
    var foo = FooSubClass1() 
} 

それともあなたが言うことができます:あなたはこのような何かを行うことができます。あなたはジェネリックスを使いたいと思っていますが、ジェネリックメソッドの中身のハードコードされたセットを持っているようです。なぜただ1つだけ作成し、閉鎖に渡し、あなたのswitch文の代わりに、次にFooModel

として
protocol FooModel { 
    init() 
} 

を定義していません。

func onHTTPRequestComplete<T: FooModel>(data: [String: Any], closure: ((T) -> Void)? = nil) { 
    guard let closure = closure else { return } 
    closure(T()) 
} 

または、なぜの外Tを作成し、それを通過していませんか?

onHTTPRequestComplete(data: data: model: FooSubClass1()) { model in 
    // Do something awesome with `model` right here. 
} 

しかし、私はおそらくやると思いますが、この次のとおりです。次に

func httpGet(callback: (([String: Any]) -> Void)? = nil) { 
    // Do some network stuff and get the data 
    callback?(data) 
} 

callbackの内側、ちょうどあなたが好きなタイプを作成:

httpGet { data in 
    let foo = FooSubClass1(data: data) 
    // foo on you 
} 
+0

精巧な答えをありがとう!私はあなたの第1と第4のコード例の変種に行くことができると思った。しかし、問題は今どこから 'T 'の具体的なタイプを得るのですか?私はコールクロージャーで具体的なモデルタイプを定義すると想像しましたが、それはそこからそれを理解するでしょうが、そうではないようです。 'onHTTPRequestComplete'の中のTの型は常に' FooModel'として評価されます。 – BadmintonCat

+0

心配しないで!これは動作します!その間に呼び出されたいくつかのメソッドのメソッドシグネチャを変更するのを忘れてしまった。 – BadmintonCat

+0

まあ、ほとんど働いた。 'closure(foo):Error :(@lvalue FooSubClass1) - > Voidは(T) - > Void'に変換できません。私は上記の新しいコードを更新しました。 – BadmintonCat

関連する問題