Предположим, на данный момент мы настраиваем ваш протокол, чтобы добавить процедуру, которая использует связанный тип:
public protocol RequestType: class {
associatedtype Model
var path: String { get set }
func frobulateModel(aModel: Model)
}
И Swift позволил вам создать массив RequestType
так, как вы хотите. Я мог бы передать массив этих типов запросов в функцию:
func handleQueueOfRequests(queue: [RequestType]) {
for request in queue {
request.frobulateModel()
}
}
Я дохожу до того, что хочу заморозить все, но мне нужно знать, какой тип аргумента передать в вызов. Некоторые из моих RequestType
сущностей могли взять LegoModel
, некоторые могли взять PlasticModel
, а другие могли взять PeanutButterAndPeepsModel
. Swift недоволен двусмысленностью, поэтому он не позволит вам объявить переменную протокола, имеющего связанный тип.
В то же время имеет смысл, например, создать массив, RequestType
когда мы ЗНАЕМ, что все они используют класс LegoModel
. Это кажется разумным, и это так, но вам нужен способ выразить это.
Один из способов сделать это - создать класс (или структуру, или перечисление), который связывает реальный тип с именем абстрактного типа модели:
class LegoRequestType: RequestType {
typealias Model = LegoModel
}
Теперь вполне разумно объявить массив, LegoRequestType
потому что, если бы мы хотели их frobulate
все, мы знали бы, что нам пришлось бы LegoModel
каждый раз передавать .
Этот нюанс со связанными типами делает любой протокол, который их использует, особенным. Стандартная библиотека Swift имеет такие протоколы, в первую очередь, Collection
или Sequence
.
Чтобы позволить вам создать массив вещей, реализующих Collection
протокол, или набор вещей, реализующих протокол последовательности, Стандартная библиотека использует технику, называемую «стирание типа», для создания типов структур AnyCollection<T>
или AnySequence<T>
. Технику стирания типа довольно сложно объяснить в ответе на переполнение стека, но если вы поищете в Интернете, есть много статей об этом.
Я могу порекомендовать видео Алекса Галлахера о протоколах со связанными типами (PAT) на YouTube.
Из 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()
источник
Небольшое изменение дизайна вашего кода могло бы сделать это возможным. Добавьте пустой протокол, не связанный с типом, наверху иерархии протоколов. Как это...
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)
источник
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 } }
И пример контроллера представления:
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 }
источник
Эта ошибка также может возникнуть в следующем сценарии:
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 }
источник