프로토콜은 Self 또는 associatedType 요구 사항이 있으므로 일반 제약 조건으로 만 사용할 수 있습니다.


103

프로토콜 RequestType이 있고 아래와 같이 associatedType Model이 있습니다.

public protocol RequestType: class {

    associatedtype Model
    var path: String { get set }

}

public extension RequestType {

    public func executeRequest(completionHandler: Result<Model, NSError> -> Void) {
        request.response(rootKeyPath: rootKeyPath) { [weak self] (response: Response<Model, NSError>) -> Void in
            completionHandler(response.result)
            guard let weakSelf = self else { return }
            if weakSelf.logging { debugPrint(response) }
        }
    }

}

이제 모든 실패한 요청의 대기열을 만들려고합니다.

public class RequestEventuallyQueue {

    static let requestEventuallyQueue = RequestEventuallyQueue()
    let queue = [RequestType]()

}

그러나 let queue = [RequestType]()Protocol RequestType은 Self 또는 AssociatedType 요구 사항이 있기 때문에 일반 제약 조건으로 만 사용할 수 있다는 오류가 있습니다.

답변:


152

관련 유형을 사용하는 루틴을 추가하기 위해 프로토콜을 조정하는 순간을 가정 해보십시오.

public protocol RequestType: class {
    associatedtype Model
    var path: String { get set }

    func frobulateModel(aModel: Model)
}

그리고 Swift는 RequestType여러분이 원하는 방식으로 배열을 만들 수 있도록 했습니다. 이러한 요청 유형의 배열을 함수에 전달할 수 있습니다.

func handleQueueOfRequests(queue: [RequestType]) {
    // frobulate All The Things!

    for request in queue {
       request.frobulateModel(/* What do I put here? */)
    }
}

나는 모든 것을 표현하고 싶은 시점에 이르렀지만 어떤 유형의 인수를 호출에 전달할지 알아야합니다. 내 RequestType엔티티 중 일부 는를 LegoModel, 일부는를 PlasticModel, 다른 일부 는 PeanutButterAndPeepsModel. Swift는 모호함에 만족하지 않으므로 연관된 유형이있는 프로토콜의 변수를 선언 할 수 없습니다.

동시에, 예를 들어 RequestType우리가 모두 LegoModel. 이것은 합리적으로 보이지만, 그것을 표현할 방법이 필요합니다.

이를 수행하는 한 가지 방법은 실제 유형을 추상 모델 유형 이름과 연결하는 클래스 (또는 구조체 또는 열거 형)를 만드는 것입니다.

class LegoRequestType: RequestType {
  typealias Model = LegoModel

  // Implement protocol requirements here
}

이제 배열을 선언하는 것이 전적으로 합리적입니다. LegoRequestType왜냐하면 frobulate모든 배열을 원한다면 LegoModel매번 전달해야한다는 것을 알고 있기 때문 입니다.

연관 유형과의 이러한 뉘앙스는이를 사용하는 모든 프로토콜을 특별하게 만듭니다. Swift Standard Library에는 가장 주목할만한 Collection또는 Sequence.

Collection프로토콜 을 구현하는 사물의 배열 또는 시퀀스 프로토콜을 구현하는 사물의 집합 을 만들 수 있도록 표준 라이브러리는 "유형 삭제"라는 기술을 사용하여 구조체 유형 AnyCollection<T>또는 AnySequence<T>. 유형 삭제 기술은 Stack Overflow 답변에서 설명하기에는 다소 복잡하지만 웹을 검색하면 관련 기사가 많이 있습니다.

YouTube의 PAT (Protocols With Associated Types)대한 Alex Gallagher 의 비디오를 추천 할 수 있습니다 .


41
"솔루션은 매우 일반적인 😂"
아돌 포

6
이것은 내가이 문제에 대해 본 최고의 설명 중 하나입니다
Keab42

1
그래서 좋은 설명, 그래서 하나의 대답.
Almas Adilbek

1
어떻게합니까 frobulate 평균을?
Mofawaw

1
이 대답은 frobulate라는 단어의 사용을 비난하는 큰 변명이었습니다.
ScottyBlades

19

Swift 5.1에서-Xcode 11

불투명 한 결과 유형을 사용하여 이와 같은 결과를 얻을 수 있습니다 .

이것을 상상하십시오 :

protocol ProtocolA {
    associatedtype number
}

class ClassA: ProtocolA {
    typealias number = Double
}

따라서 다음은 오류를 생성합니다.

var objectA: ProtocolA = ClassA() /* Protocol can only be used as a generic constraint because it has Self or associatedType requirements */

그러나 유형 앞에 키워드 를 추가 하여 유형을 불투명 하게 만들면 some문제가 해결되며 일반적으로 이것이 우리가 원하는 유일한 것입니다.

var objectA: some ProtocolA = ClassA()

5

스위프트 5.1

관련 유형기본 프로토콜 을 구현하여 일반 프로토콜 을 사용하는 방법 의 :

import Foundation

protocol SelectOptionDataModelProtocolBase: class{}

protocol SelectOptionDataModelProtocol: SelectOptionDataModelProtocolBase {
    associatedtype T
    
    var options: Array<T> { get }
    
    var selectedIndex: Int { get set }
    
}

class SelectOptionDataModel<A>: SelectOptionDataModelProtocol {
    typealias T = A
    
    var options: Array<T>
    
    var selectedIndex: Int
    
    init(selectedIndex _selectedIndex: Int, options _options: Array<T>) {
        self.options = _options
        self.selectedIndex = _selectedIndex
    }
    
}

그리고보기 컨트롤러의 예 :

import UIKit

struct Car {
    var name: String?
    var speed: Int?
}

class SelectOptionViewController: UIViewController {
    
    // MARK: - IB Outlets
    
    // MARK: - Properties
    
    var dataModel1: SelectOptionDataModelProtocolBase?
    var dataModel2: SelectOptionDataModelProtocolBase?
    var dataModel3: SelectOptionDataModelProtocolBase?

    // MARK: - Initialisation
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    convenience init() {
        self.init(title: "Settings ViewController")
    }
    
    init(title _title: String) {
        super.init(nibName: nil, bundle: nil)
        
        self.title = _title
        
        self.dataModel1 = SelectOptionDataModel<String>(selectedIndex: 0, options: ["option 1", "option 2", "option 3"])
        self.dataModel2 = SelectOptionDataModel<Int>(selectedIndex: 0, options: [1, 2, 3])
        self.dataModel3 = SelectOptionDataModel<Car>(selectedIndex: 0, options: [Car(name: "BMW", speed: 90), Car(name: "Toyota", speed: 60), Car(name: "Subaru", speed: 120)])

    }
    
    // MARK: - IB Actions
    
    
    // MARK: - View Life Cycle

    
}

4

코드 디자인을 약간만 변경하면 가능할 수 있습니다. 프로토콜 계층의 맨 위에 비어있는 연관되지 않은 유형의 프로토콜을 추가하십시오. 이렇게 ...

public protocol RequestTypeBase: class{}

public protocol RequestType: RequestTypeBase {

    associatedtype Model
    var path: Model? { get set } //Make it type of Model

}
public class RequestEventuallyQueue {

    static let requestEventuallyQueue = RequestEventuallyQueue()
    var queue = [RequestTypeBase]() //This has to be 'var' not 'let'

}

또 다른 예는 RequestType 프로토콜에서 파생 된 클래스를 사용하여 대기열을 만들고 대기열을 함수에 전달하여 적절한 유형을 인쇄하는 것입니다.

public class RequestA<AType>: RequestType{
   public typealias Model = AType
   public var path: AType?
}
public class RequestB<BType>: RequestType{
   public typealias Model = BType
   public var path: BType?
}

var queue = [RequestTypeBase]()

let aRequest: RequestA = RequestA<String>()
aRequest.path = "xyz://pathA"

queue.append(aRequest)

let bRequest: RequestB = RequestB<String>()
bRequest.path = "xyz://pathB"

queue.append(bRequest)

let bURLRequest: RequestB = RequestB<URL>()
bURLRequest.path = URL(string: "xyz://bURLPath")

queue.append(bURLRequest)

func showFailed(requests: [RequestTypeBase]){

    for request in requests{
        if let request = request as? RequestA<String>{
            print(request.path!)
        }else if let request = request as? RequestB<String>{
            print(request.path!)
        }else if let request = request as? RequestB<URL>{
            print(request.path!)
        }

    }
}

showFailed(requests: queue)

0

이 오류는 다음 시나리오에서도 발생할 수 있습니다.

protocol MyProtocol {
    assosciatedtype SomeClass
    func myFunc() -> SomeClass
}

struct MyStuct {
    var myVar = MyProtocol
}

이 경우 문제를 해결하기 위해해야 ​​할 일은 제네릭을 사용하는 것입니다.

protocol MyProtocol {
    assosciatedtype SomeClass
    func myFunc() -> SomeClass
}

struct MyStuct<T: MyProtocol> {
    var myVar = T
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.