Selamat siang teman Khusus untuk siswa kursus
"Pengembang iOS. Advanced Course ", kami menyiapkan terjemahan bagian kedua dari artikel" The Power of Generics in Swift ".

Jenis terkait, tempat klausa, subskrip, dan lainnya ...Dalam artikel
"The Power of Generics in Swift. Bagian 1 β menjelaskan fungsi generik, tipe generik, dan batasan tipe. Jika Anda seorang pemula, saya sarankan Anda membaca bagian pertama untuk pemahaman yang lebih baik.
Saat mendefinisikan protokol, terkadang berguna untuk mendeklarasikan satu atau lebih tipe terkait sebagai bagian dari definisi. Tipe terkait menentukan nama rintisan untuk tipe yang digunakan sebagai bagian dari protokol. Tipe aktual yang digunakan untuk tipe terkait ini tidak akan ditentukan hingga protokol digunakan. Jenis yang
associatedtype
dideklarasikan menggunakan kata kunci tipe
associatedtype
.
Kita dapat mendefinisikan protokol untuk stack yang kita buat di bagian
pertama .
protocol Stackable { associatedtype Element mutating func push(element: Element) mutating func pop() -> Element? func peek() throws -> Element func isEmpty() -> Bool func count() -> Int subscript(i: Int) -> Element { get } }
Protokol
Stackable
mendefinisikan fungsionalitas yang diperlukan yang harus disediakan oleh setiap tumpukan.
Jenis apa pun yang sesuai dengan protokol
Stackable
harus dapat menentukan jenis nilai yang disimpannya. Ini harus memastikan bahwa hanya elemen-elemen dengan tipe yang benar yang ditambahkan ke stack, dan harus jelas tipe elemen apa yang dikembalikan oleh subskripnya.
Mari kita ubah tumpukan kita sesuai dengan protokol:
enum StackError: Error { case Empty(message: String) } protocol Stackable { associatedtype Element mutating func push(element: Element) mutating func pop() -> Element? func peek() throws -> Element func isEmpty() -> Bool func count() -> Int subscript(i: Int) -> Element { get } } public struct Stack<T>: Stackable { public typealias Element = T var array: [T] = [] init(capacity: Int) { array.reserveCapacity(capacity) } public mutating func push(element: T) { array.append(element) } public mutating func pop() -> T? { return array.popLast() } public func peek() throws -> T { guard !isEmpty(), let lastElement = array.last else { throw StackError.Empty(message: "Array is empty") } return lastElement } func isEmpty() -> Bool { return array.isEmpty } func count() -> Int { return array.count } } extension Stack: Collection { public func makeIterator() -> AnyIterator<T> { var curr = self return AnyIterator { curr.pop() } } public subscript(i: Int) -> T { return array[i] } public var startIndex: Int { return array.startIndex } public var endIndex: Int { return array.endIndex } public func index(after i: Int) -> Int { return array.index(after: i) } } extension Stack: CustomStringConvertible { public var description: String { let header = "***Stack Operations start*** " let footer = " ***Stack Operation end***" let elements = array.map{ "\($0)" }.joined(separator: "\n") return header + elements + footer } } var stack = Stack<Int>(capacity: 10) stack.push(element: 1) stack.pop() stack.push(element: 3) stack.push(element: 4) print(stack)
Memperluas jenis yang ada untuk menunjukkan jenis terkait
Anda dapat memperluas jenis yang ada untuk mematuhi protokol.
protocol Container { associatedtype Item mutating func append(_ item: Item) var count: Int { get } subscript(i: Int) -> Item { get } } extension Array: Container {}
Menambahkan kendala ke tipe terkait:
Anda dapat menambahkan batasan pada tipe terkait dalam protokol untuk memastikan bahwa tipe terkait mematuhi batasan-batasan ini.
Mari kita ubah protokol
Stackable
.
protocol Stackable { associatedtype Element: Equatable mutating func push(element: Element) mutating func pop() -> Element? func peek() throws -> Element func isEmpty() -> Bool func count() -> Int subscript(i: Int) -> Element { get } }
Sekarang tipe elemen stack harus cocok dengan
Equatable
, jika tidak, kesalahan waktu kompilasi akan terjadi.
Batasan protokol rekursif:
Protokol dapat menjadi bagian dari persyaratannya sendiri.
protocol SuffixableContainer: Container { associatedtype Suffix: SuffixableContainer where Suffix.Item == Item func suffix(_ size: Int) -> Suffix }
Suffix
memiliki dua batasan: ia harus sesuai dengan protokol
SuffixableContainer
(protokolnya didefinisikan di sini), dan tipe
Item
-nya harus cocok dengan tipe
Item
dari container.
Ada contoh yang baik di perpustakaan standar Swift di
Protocol Sequence
menggambarkan topik ini.
Usulan tentang batasan protokol rekursif:
https://github.com/apple/swift-evolution/blob/master/proposals/0157-recursive-protocol-constraints.mdEkstensi jenis umum:
Saat Anda memperluas tipe generik, Anda tidak mendeskripsikan daftar parameter tipe saat mendefinisikan ekstensi. Sebagai gantinya, daftar parameter tipe dari definisi sumber tersedia di tubuh ekstensi, dan nama-nama parameter dari tipe sumber digunakan untuk merujuk ke parameter tipe dari definisi sumber.
extension Stack { var topItem: Element? { return items.isEmpty ? nil : items[items.count - 1] } }
Generik mana klausa
Untuk jenis terkait, penting untuk mendefinisikan persyaratan. Persyaratan dijelaskan oleh
klausa generic where . Klausa generik di
where
memungkinkan Anda untuk meminta agar tipe terkait sesuai dengan protokol tertentu atau bahwa parameter tipe tertentu dan tipe terkait adalah sama. Klausa generik di
where
dimulai dengan kata kunci
where
, diikuti oleh batasan untuk tipe terkait atau hubungan kesetaraan antara tipe dan tipe terkait. Klausa Generik di
where
ditulis tepat sebelum penjepit pembuka dari tipe atau fungsi tubuh.
func allItemsMatch<C1: Container, C2: Container> (_ someContainer: C1, _ anotherContainer: C2) -> Bool where C1.Item == C2.Item, C1.Item: Equatable { }
Ekstensi dengan kondisi Generik di mana
Anda dapat menggunakan klausa generik
where
sebagai bagian dari ekstensi. Contoh berikut memperluas struktur
Stack
keseluruhan dari contoh sebelumnya dengan menambahkan metode
isTop (_ :)
.
extension Stack where Element: Equatable { func isTop(_ item: Element) -> Bool { guard let topItem = items.last else { return false } return topItem == item } }
Ekstensi menambahkan metode
isTop (_ :)
hanya ketika item pada stack mendukung Equatable. Anda juga dapat menggunakan klausa generik
where
dengan ekstensi protokol. Anda dapat menambahkan beberapa persyaratan ke
where
memisahkannya dengan koma.
Jenis terkait dengan klausa Generik di mana:
Anda bisa menyertakan klausa generik di
where
dalam tipe terkait.
protocol Container { associatedtype Item mutating func append(_ item: Item) var count: Int { get } subscript(i: Int) -> Item { get } associatedtype Iterator: IteratorProtocol where Iterator.Element == Item func makeIterator() -> Iterator }
Untuk protokol yang mewarisi dari protokol lain, Anda menambahkan batasan untuk tipe terikat yang diwarisi, termasuk klausa generik di
where
dalam deklarasi protokol. Misalnya, kode berikut mendeklarasikan protokol
ComparableContainer
yang mengharuskan
Item
mendukung
Comparable
:
protocol ComparableContainer: Container where Item: Comparable { }
Alias ββtipe umum:
Jenis alias dapat memiliki parameter umum. Itu masih akan menjadi alias (artinya, itu tidak akan memperkenalkan tipe baru).
typealias StringDictionary<Value> = Dictionary<String, Value> var d1 = StringDictionary<Int>() var d2: Dictionary<String, Int> = d1
Dalam mekanisme ini, batasan tambahan untuk parameter tipe tidak dapat digunakan.
Kode seperti itu tidak akan berfungsi:
typealias ComparableArray<T where T : Comparable> = Array<T>
Superskrip generik
Subskrip dapat menggunakan mekanisme generik dan menyertakan klausa generic
where
. Anda menulis nama jenis dalam kurung sudut setelah
subscript
, dan menulis generik di
where
klausa segera sebelum kurung pembuka badan subskrip.
extension Container { subscript<Indices: Sequence>(indices: Indices) -> [Item] where Indices.Iterator.Element == Int { var result = [Item]() for index in indices { result.append(self[index]) } return result } }
Spesialisasi generik
Spesialisasi generik berarti bahwa kompiler mengkloning tipe atau fungsi generik, seperti Stack
<
T
>
, untuk tipe parameter tertentu, seperti Int. Fungsi khusus ini kemudian dapat dioptimalkan khusus untuk Int, sementara semua yang berlebihan akan dihapus. Proses penggantian parameter tipe dengan argumen tipe pada waktu kompilasi disebut
spesialisasi .
Dengan mengkhususkan fungsi generik untuk jenis ini, kita dapat menghilangkan biaya pengiriman virtual, panggilan inline, bila perlu, dan menghilangkan overhead sistem generik.
Operator Kelebihan
Tipe generik tidak berfungsi dengan operator secara default, untuk ini Anda memerlukan protokol.
func ==<T: Equatable>(lhs: Matrix<T>, rhs: Matrix<T>) -> Bool { return lhs.array == rhs.array }
Suatu hal yang menarik tentang obat generik
Mengapa Anda tidak bisa mendefinisikan properti tersimpan
statis untuk tipe generik?
Ini akan membutuhkan penyimpanan properti yang terpisah untuk setiap spesialisasi generik (T) individu.
Sumber daya untuk studi mendalam:
https://github.com/apple/swift/blob/master/docs/Generics.rsthttps://github.com/apple/swift/blob/master/docs/GenericsManifesto.mdhttps://developer.apple.com/videos/play/wwdc2018/406/https://www.youtube.com/watch?v=ctS8FzqcRughttps://medium.com/@vhart/protocols-generics-and-existential-containers-wait-what-e2e698262ab1
Itu saja. Sampai jumpa di
lapangan .