Swift:ジェネリック型のインスタンスをクラス変数に割り当てますか?

Aug 20 2020

私はこれを使おうとしています https://github.com/bricklife/JSONRPCKit。これは、Swift用のJSONRPCの単純な実装です。

これはreadmeの例です。非常に簡単です。

// Generating request JSON
let batchFactory = BatchFactory(version: "2.0", idGenerator: NumberIdGenerator())
let request = Subtract(minuend: 42, subtrahend: 23)
let batch = batchFactory.create(request)
batch.requestObject // ["jsonrpc": "2.0", "method": "subtract", "params": [42, 23], "id": 1]

// Parsing response JSON
let responseObject: Any = ["jsonrpc": "2.0", "result": 19, "id": 1]
let response = try! batch.responses(from: responseObject)

batchクラスの関数で作成されたとしても、追跡したいと思います。そのため、別の関数(サーバーから応答を受け取ったとき)。特定のリクエスト/バッチ/バッチ要素にアクセスして、必要な機能を実行できます。

JSONRPCKitのタイプを使用して、クラスに変数を作成できません。

次の行に沿ってコンパイラエラーが発生します。

プロトコル「バッチ」は、自己または関連する型の要件があるため、ジェネリック制約としてのみ使用できます。

ジェネリック型 'Batch1'への参照には、<...>の引数が必要です

プロトコルタイプ「リクエスト」の値は「リクエスト」に準拠できません。struct / enum / classタイプのみがプロトコルに準拠できます

問題を回避するために、関数でジェネリックを使用しようとしましたが、それも役に立ちませんでした。

    func store_b<Batch: JSONRPCKit.Batch>(_ batch: Batch){

詳細:batchタイプBatch1は次のようになります:

public struct Batch1<Request: JSONRPCKit.Request>: Batch {

Batchはプロトコルです

public protocol Batch {

batchリクエストを追跡してレスポンスを取得する簡単な方法はありますか/これらのジェネリックを適切に使用するにはどうすればよいですか?

回答

Max Aug 20 2020 at 03:16

createメソッドのシグネチャとBatch1タイプを分類します

public func create<Request: JSONRPCKit.Request>(_ request: Request) -> Batch1<Request>

public struct Batch1<Request: JSONRPCKit.Request>: Batch {
    public typealias Responses = Request.Response
    public typealias Results = Result<Request.Response, JSONRPCError>
}

createは、任意のタイプの単一のパラメーターを受け取るジェネリック関数です。制約<Request: JSONRPCKit.Request>は、パラメーターのタイプがプロトコルに準拠している必要があることを指定しますJSONRPCKit.Request

Batch1は、2つの内部型を定義する必要があるジェネリック構造体であり、どちらも任意の型に関連付けられています。ここでも、<Request: JSONRPCKit.Request>その任意の型がプロトコルに準拠する必要があることを指定しますJSONRPCKit.Request

戻り値の型Batch1<Request>は、これら2つのジェネリックスを結び付け、返されるBatch1構造体に使用される型がrequestパラメーターの型と一致することを示します。

createメソッドを呼び出すときは、例のように、ジェネリック型に具象型を入力する必要があります

let batch = batchFactory.create(Subtract(minuend: 42, subtrahend: 23))

これで、コンパイラはすべての定義を調べて、そのタイプの具体的な実装を作成できます。

public func create(_ request: Subtract) -> Batch1<Subtract>

public struct Batch1<Subtract>: Batch {
    public typealias Responses = Int
    public typealias Results = Result<Int, JSONRPCError>
}

これは、をSubtract定義するファクトを使用しますtypealias Response = Int。もはや一般的なものはないことに注意してください。これらはすべて具体的なタイプです。タイプのプロパティを保存しようとしても問題はありませんBatch1<Subtract>


これが、バッチをプロパティに簡単に保存できない理由です。Swiftは、どのタイプをプロパティに入れるべきかわかりません。

これを回避する1つの方法は、代わりにクロージャを格納することです。これにより、クラスがそれについて知る必要がないように、汎用バッチをラップできます。

// closure property
var responseProcessor: ((Any) -> Void)?

func createBatch<R: JSONRPCKit.Request>(request: R, processor: @escaping (R.Response) -> Void) {
    let batch = batchFactory.create(request)
    self.responseProcessor = { responseObject in
        let response = try! batch.responses(from: responseObject)
        processor(response)
    }
}

// somewhere else when you get the responseObject
responseProcessor?(responseObject)

このメソッドは、ジェネリック型に一致する特定のクロージャを取得し、ジェネリックに依存しなくなったクロージャでラップします。このようにして、すべてのバッチが同じクロージャープロパティを共有できます。