プロトコルにはSelfまたはassociatedType要件があるため、ジェネリック制約としてのみ使用できます。


101

私はプロトコルRequestTypeを持っていて、それは以下のようにassociatedTypeモデルを持っています。

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]()SelfまたはassociatedType要件があるため、ProtocolRequestTypeはジェネリック制約としてのみ使用できるというエラーがオンラインで表示されます。

回答:


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。これは理にかなっているように思えますが、それを表現するには何らかの方法が必要です。

これを行う1つの方法は、実際の型を抽象モデル型名に関連付けるクラス(または構造体、または列挙型)を作成することです。

class LegoRequestType: RequestType {
  typealias Model = LegoModel

  // Implement protocol requirements here
}

の配列を宣言することは完全に合理的です。LegoRequestTypeなぜならfrobulate、それらすべてを宣言したい場合は、LegoModel毎回渡す必要があることがわかっているからです。

関連するタイプとのこのニュアンスは、それらを使用するプロトコルを特別なものにします。スウィフト標準ライブラリは、最も顕著なの、このようなプロトコルを持っていますCollectionSequence

Collectionプロトコルを実装するものの配列またはシーケンスプロトコルを実装するもののセットを作成できるようにするために、標準ライブラリは「型消去」と呼ばれる手法を使用して構造体タイプAnyCollection<T>またはを作成しますAnySequence<T>。型消去手法は、スタックオーバーフローの回答で説明するのはかなり複雑ですが、Webを検索すると、それに関する記事がたくさんあります。

YouTubeの関連タイプのプロトコル(PAT)に関するAlexGallagherのビデオをお勧めします。


40
「あなたのソリューションは非常に一般的です」😂–
Adolfo

6
これは、この問題について私が見た中で最も良い説明の1つです
Keab42 2017年

1
とても良い説明なので、単一の答えです。
AlmasAdilbek18年

1
フロビュレートとはどういう意味ですか?
モファワウ

1
この答えは、frobulateという単語の使用法をfrobulateするための素晴らしい言い訳でした。
ScottyBlades

17

Swift5.1から-Xcode11

不透明な結果タイプを使用して、そのようなことを実現できます。

これを想像してください:

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

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)

4

Swift 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
    }
    
}

そして、ViewControllerの例:

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

    
}

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.