プロトコルRequestTypeがあり、次のようなrelatedTypeモデルがあります。
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]()
行でエラーが発生します。ProtocolRequestTypeはSelfまたはrelatedTypeの要件があるため、汎用制約としてのみ使用できます。
とりあえず、プロトコルを調整して、関連するタイプを使用するルーチンを追加するとします。
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
を渡す必要があることがわかっているからです。
関連付けられたタイプのこのニュアンスにより、それらを使用するプロトコルは特別なものになります。 Swift標準ライブラリには、このようなプロトコルで最も注目すべきはCollection
またはSequence
です。
Collection
プロトコルを実装するものの配列またはシーケンスプロトコルを実装するもののセットを作成できるようにするために、標準ライブラリは「type-erasure」と呼ばれる手法を使用して、構造型AnyCollection<T>
またはAnySequence<T>
を作成します。型消去の手法はStack Overflowの回答で説明するのはかなり複雑ですが、Webを検索すると、それに関する記事がたくさんあります。
YouTubeの 関連タイプ(PAT)のプロトコルに関するアレックスギャラガー からビデオをお勧めします。
コードの設計に少し変更を加えると、それが可能になります。プロトコル階層の最上部に、空の、関連付けられていないタイプのプロトコルを追加します。このような...
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)