Misalkan untuk saat ini kami menyesuaikan protokol Anda untuk menambahkan rutinitas yang menggunakan tipe terkait:
public protocol RequestType: class {
associatedtype Model
var path: String { get set }
func frobulateModel(aModel: Model)
}
Dan Swift mengizinkan Anda membuat larik RequestType
sesuai keinginan Anda. Saya bisa melewatkan berbagai jenis permintaan tersebut ke dalam sebuah fungsi:
func handleQueueOfRequests(queue: [RequestType]) {
for request in queue {
request.frobulateModel()
}
}
Saya sampai pada titik bahwa saya ingin mengacaukan semua hal, tetapi saya perlu tahu jenis argumen apa yang harus dimasukkan ke dalam panggilan. Beberapa RequestType
entitas saya dapat mengambil LegoModel
, beberapa dapat mengambil PlasticModel
, dan lainnya dapat mengambil PeanutButterAndPeepsModel
. Swift tidak senang dengan ambiguitas tersebut sehingga tidak akan membiarkan Anda mendeklarasikan variabel protokol yang memiliki tipe terkait.
Pada saat yang sama, sangat masuk akal untuk, misalnya, membuat larik RequestType
ketika kita TAHU bahwa semuanya menggunakan LegoModel
. Ini tampaknya masuk akal, dan memang demikian, tetapi Anda memerlukan cara untuk mengungkapkannya.
Salah satu cara untuk melakukannya adalah dengan membuat kelas (atau struct, atau enum) yang mengaitkan tipe nyata dengan nama tipe Model abstrak:
class LegoRequestType: RequestType {
typealias Model = LegoModel
}
Sekarang sepenuhnya masuk akal untuk mendeklarasikan sebuah array LegoRequestType
karena jika kita ingin frobulate
semuanya, kita tahu kita harus melewatkannya LegoModel
setiap saat.
Nuansa dengan Jenis Terkait ini membuat protokol apa pun yang menggunakannya menjadi istimewa. Perpustakaan Standar Swift memiliki Protokol seperti ini yang paling terkenal Collection
atau Sequence
.
Untuk memungkinkan Anda membuat larik hal-hal yang mengimplementasikan Collection
protokol atau sekumpulan hal-hal yang mengimplementasikan protokol sekuens, Perpustakaan Standar menggunakan teknik yang disebut "jenis-penghapusan" untuk membuat jenis struktur AnyCollection<T>
atau AnySequence<T>
. Teknik penghapusan jenis agak rumit untuk dijelaskan dalam jawaban Stack Overflow, tetapi jika Anda menelusuri web, ada banyak artikel tentangnya.
Saya dapat merekomendasikan video dari Alex Gallagher tentang Protocols With Associated Types (PATs) di YouTube.
Dari Swift 5.1 - Xcode 11
Anda dapat menggunakan jenis hasil buram untuk mencapai sesuatu seperti itu.
bayangkan ini:
protocol ProtocolA { associatedtype number } class ClassA: ProtocolA { typealias number = Double }
Jadi yang berikut ini menghasilkan kesalahan:
var objectA: ProtocolA = ClassA() /* Protocol can only be used as a generic constraint because it has Self or associatedType requirements */
Tetapi membuat tipe menjadi buram dengan menambahkan
some
kata kunci sebelum tipe akan memperbaiki masalah dan biasanya hanya itulah yang kami inginkan:var objectA: some ProtocolA = ClassA()
sumber
Swift 5.1.0
Sebuah contoh bagaimana Anda dapat menggunakan protokol generik dengan menerapkan jenis terkait dan protokol dasar :
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 } }
Dan contoh View Controller:
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 }
sumber
Sedikit perubahan dalam desain kode Anda dapat memungkinkannya. Tambahkan protokol kosong, jenis tidak terkait, di bagian atas hierarki protokol Anda. Seperti ini...
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' }
Contoh lain, dengan kelas-kelas yang diturunkan dari protokol RequestType, membuat antrian dan meneruskan antrian ke suatu fungsi untuk mencetak tipe yang sesuai.
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)
sumber
Galat ini juga dapat terjadi dalam skenario berikut ini:
protocol MyProtocol { assosciatedtype SomeClass func myFunc() -> SomeClass } struct MyStuct { var myVar = MyProtocol }
Dalam kasus ini, yang harus Anda lakukan untuk memperbaiki masalah ini adalah menggunakan obat generik:
protocol MyProtocol { assosciatedtype SomeClass func myFunc() -> SomeClass } struct MyStuct<T: MyProtocol> { var myVar = T }
sumber