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의 유형을 사용하여 내 클래스에서 변수를 만들 수 없습니다.

다음 줄을 따라 컴파일러 오류가 발생합니다.

프로토콜 'Batch'는 자체 또는 관련 유형 요구 사항이 있으므로 일반 제약으로 만 사용할 수 있습니다.

제네릭 유형 'Batch1'에 대한 참조에는 <...>에 인수가 필요합니다.

프로토콜 유형 'Request'의 값은 'Request'를 준수 할 수 없습니다. struct / enum / class 유형 만 프로토콜을 준수 할 수 있습니다.

어떻게 든 내 문제를 피하기 위해 제네릭 함수를 사용하려고 시도했지만 도움이되지 않았습니다.

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

더 많은 정보 : batchis of type Batch1, like so :

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두 가지 내부 유형을 정의해야하는 일반 구조체입니다. 둘 다 임의 유형 과 연결되어 있습니다 . 다시 말하지만, <Request: JSONRPCKit.Request>임의의 유형이 프로토콜을 준수해야 함을 지정합니다 JSONRPCKit.Request.

반환 유형 Batch1<Request>은이 두 제네릭을 함께 묶어 반환 된 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)

이 메서드는 제네릭 유형과 일치하는 특정 클로저를 가져와 더 이상 제네릭에 종속되지 않는 클로저로 래핑합니다. 이렇게하면 모든 배치가 동일한 클로저 속성을 공유 할 수 있습니다.