Swift: กำหนดอินสแตนซ์ของประเภททั่วไปให้กับตัวแปรคลาส?

Aug 20 2020

ฉันกำลังพยายามใช้สิ่งนี้ https://github.com/bricklife/JSONRPCKit. เป็นการนำ JSONRPC สำหรับ Swift ไปใช้งานง่ายๆ

นี่คือตัวอย่างใน 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แม้ว่ามันจะถูกสร้างขึ้นในฟังก์ชันของคลาสก็ตาม เพื่อให้ฟังก์ชั่นอื่น (เมื่อฉันได้รับคำตอบจากเซิร์ฟเวอร์ของฉัน) สามารถเข้าถึงคำขอ / แบทช์ / แบทช์เฉพาะและเรียกใช้ฟังก์ชันใดก็ได้ที่ต้องการ

ฉันไม่สามารถสร้าง vars ในชั้นเรียนของฉันโดยใช้ประเภทใดก็ได้จาก JSONRPCKit

ฉันได้รับข้อผิดพลาดของคอมไพเลอร์ตามบรรทัดของ:

โปรโตคอล 'Batch' สามารถใช้เป็นข้อ จำกัด ทั่วไปเท่านั้นเนื่องจากมีข้อกำหนดในตัวเองหรือประเภทที่เกี่ยวข้อง

การอ้างอิงถึงประเภททั่วไป 'Batch1' ต้องการอาร์กิวเมนต์ใน <... >

ค่าของโปรโตคอลประเภท 'Request' ไม่สามารถสอดคล้องกับ 'Request'; มีเพียงประเภท 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

แยกลายเซ็นวิธีการสร้างและประเภท 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เป็น struct ทั่วไปที่ตอบสนองความต้องการที่จะกำหนดสองประเภทภายในซึ่งทั้งสองมีความเกี่ยวข้องกับบางประเภทโดยพลการ อีกครั้งระบุว่าประเภทโดยพลการจะต้องสอดคล้องกับโปรโตคอล<Request: JSONRPCKit.Request>JSONRPCKit.Request

ประเภทการส่งคืนจะBatch1<Request>เชื่อมโยงทั้งสอง generics เข้าด้วยกันโดยกล่าวว่าประเภทที่ใช้สำหรับโครงสร้างที่ส่งคืน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 ไม่รู้ว่าจะใส่ประเภทใดลงไป!

วิธีหนึ่งในการแก้ปัญหานี้คือแทนที่จะเก็บการปิดซึ่งสามารถห่อแบทช์ทั่วไปที่ชั้นเรียนไม่จำเป็นต้องรู้

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

วิธีนี้ใช้การปิดเฉพาะที่ตรงกับประเภททั่วไปและปิดด้วยการปิดที่ไม่ขึ้นอยู่กับแบบทั่วไปอีกต่อไป วิธีนี้ทุกชุดสามารถแชร์คุณสมบัติการปิดเดียวกันได้