Swift: Tetapkan sebuah instance dari tipe generik ke variabel kelas?

Aug 20 2020

Saya mencoba menggunakan ini https://github.com/bricklife/JSONRPCKit. Ini adalah implementasi sederhana JSONRPC untuk Swift.

Ini adalah contoh di readme. Cukup sederhana.

// 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)

Saya ingin melacak batch, bahkan jika itu dibuat dalam fungsi kelas. Sehingga berfungsi lain (ketika saya mendapat balasan dari server saya). Dapat mengakses permintaan / batch / batchelement spesifik dan menjalankan fungsi apa pun yang diperlukan.

Saya tidak dapat membuat vars di kelas saya menggunakan salah satu jenis dari JSONRPCKit.

Saya mendapatkan kesalahan kompiler di sepanjang baris:

Protokol 'Batch' hanya dapat digunakan sebagai batasan umum karena memiliki persyaratan tipe Self atau terkait

Referensi ke tipe generik 'Batch1' membutuhkan argumen di <...>

Nilai tipe protokol 'Request' tidak bisa mengikuti 'Request'; hanya tipe struct / enum / class yang dapat mengikuti protokol

Saya mencoba menggunakan generik dalam fungsi entah bagaimana untuk menghindari masalah saya tetapi itu juga tidak membantu.

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

Info lebih lanjut: batchbertipe Batch1, seperti:

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

dan Batchmerupakan protokol

public protocol Batch {

Apakah ada cara sederhana untuk melacak batchpermintaan saya dan mendapatkan tanggapan / bagaimana saya menggunakan obat generik ini dengan benar?

Jawaban

Max Aug 20 2020 at 03:16

Hancurkan tanda tangan metode pembuatan dan tipe 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>
}

createadalah fungsi umum yang menggunakan satu parameter jenis apa pun . Batasan <Request: JSONRPCKit.Request>menentukan bahwa jenis parameter harus sesuai dengan protokol JSONRPCKit.Request.

Batch1adalah struct umum yang perlu mendefinisikan dua tipe internal, keduanya terkait dengan beberapa tipe arbitrer . Sekali lagi, <Request: JSONRPCKit.Request>tentukan bahwa jenis arbitrer harus sesuai dengan protokol JSONRPCKit.Request.

Jenis yang dikembalikan Batch1<Request>mengikat kedua generik ini bersama-sama, mengatakan bahwa jenis yang digunakan untuk Batch1struct yang dikembalikan akan cocok dengan jenis requestparameter.

Saat Anda memanggil metode create, Anda harus mengisi tipe generik dengan tipe konkret, seperti pada contoh

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

Sekarang kompilator dapat memeriksa semua definisi dan membuat implementasi konkret untuk jenis itu:

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

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

Ini menggunakan fakta yang Subtractmendefinisikan typealias Response = Int. Perhatikan bahwa tidak ada yang generik lagi; ini semua adalah tipe beton. Anda tidak akan kesulitan mencoba menyimpan properti bertipe Batch1<Subtract>.


Inilah sebabnya mengapa Anda tidak dapat dengan mudah menyimpan batch di properti: Swift tidak tahu tipe apa yang harus dimasukkan ke dalamnya!

Salah satu cara untuk mengatasinya adalah dengan menyimpan closure, ini bisa membungkus batch generik sehingga kelas tidak perlu mengetahuinya

// 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)

Metode ini mengambil closure spesifik yang cocok dengan tipe generik dan membungkusnya dalam closure yang tidak lagi bergantung pada generik. Dengan cara ini setiap batch dapat berbagi properti closure yang sama.