Generic (γενικός προγραμματισμός) — είναι ένας μηχανισμός Swift που επιτρέπει τη σύνταξη τύπων και συναρτήσεων με παραμετροποιημένο τύπο
Κύρια σημεία
Generic (γενικός τύπος) — είναι μια συντακτική κατασκευή Swift που επιτρέπει τη σύνταξη ευέλικτου, επαναχρησιμοποιήσιμου κώδικα με παραμετροποίηση τύπου. Αντί για συγκεκριμένο τύπο, γράφεται ένα placeholder σε γωνιακές αγκύλες — συνήθως
Χωρίς generic θα έπρεπε να γράφονται ξεχωριστές συναρτήσεις για κάθε τύπο: swapInts, swapStrings, swapDates. Η generic συνάρτηση swap αντικαθιστά και τις τρεις παραλλαγές με μία δήλωση. Αυτό μειώνει την αντιγραφή, αυξάνει την αναγνωσιμότητα και μειώνει την πιθανότητα σφαλμάτων κατά την αντιγραφή κώδικα.
func swapValues<T>(_ a: inout T, _ b: inout T) {
let temp = a
a = b
b = temp
}
var x = 10
var y = 20
swapValues(&x, &y)
Ο μεταγλωττιστής Swift παράγει εξειδικευμένο κώδικα για κάθε συγκεκριμένη χρήση generic. Αυτό σημαίνει ότι η generic συνάρτηση swapValues
Generic συναρτήσεις — είναι συναρτήσεις που δέχονται μία ή περισσότερες παραμέτρους-τύπους. Οι παράμετροι-τύποι υποδεικνύονται μετά το όνομα της συνάρτησης σε γωνιακές αγκύλες:
Μια συνάρτηση μπορεί να έχει πολλαπλές παραμέτρους generic. Για παράδειγμα, η συνάρτηση findKey
func findKey<K: Hashable, V>(
for value: V,
in dictionary: [K: V]
) -> K? where V: Equatable {
for (key, dictValue) in dictionary {
if dictValue == value {
return key
}
}
return nil
}
Οι περιορισμοί K: Hashable και where V: Equatable εγγυώνται ότι η findKey μπορεί να κληθεί μόνο με λεξικό του οποίου τα κλειδιά είναι κατακερματίσιμα και οι τιμές συγκρίσιμες. Τέτοιοι περιορισμοί δεν είναι γραφειοκρατία, αλλά αναγκαιότητα: χωρίς Hashable δεν μπορεί να γίνει ευρετηρίαση αναζήτησης βάσει κλειδιού, χωρίς Equatable δεν μπορεί να συγκριθεί το value με το dictValue.
where — είναι μια λέξη-κλειδί Swift που επιβάλλει πρόσθετους περιορισμούς στις παραμέτρους generic. Σε αντίθεση με την απλή υπόδειξη πρωτοκόλλου σε γωνιακές αγκύλες (
Η πρόταση where γράφεται μετά την υπογραφή της συνάρτησης ή του τύπου, πριν από το άνοιγμα του αγκίστρου. Στο where μπορεί να καθοριστεί ότι T: Comparable & Hashable (ταυτόχρονα δύο πρωτόκολλα), ή ότι T.U == Int (εξειδίκευση associated type).
protocol Container {
associatedtype Item
mutating func append(_ item: Item)
var count: Int { get }
}
extension Container where Item: Comparable {
func isSorted() -> Bool {
// Implementation available only if Item: Comparable
return true
}
}
Η επέκταση με where προσθέτει μεθόδους μόνο για εκείνους τους τύπους που ικανοποιούν την συνθήκη. Το Container
Associated Types (συσχετιζόμενοι τύποι) — είναι ένας τρόπος να γίνει το πρωτόκολλο γενικό χωρίς να καθορίζεται ο συγκεκριμένος τύπος στη φάση δήλωσης. Αντί για
Με τη βοήθεια του where μπορούν να δημιουργηθούν συνδέσεις μεταξύ associated types διαφορετικών πρωτοκόλλων. Για παράδειγμα, μπορεί να απαιτηθεί το Item ενός πρωτοκόλλου να ταιριάζει με το Iterator.Element ενός άλλου. Αυτό εγγυάται συμβατότητα τύπων σε επίπεδο μεταγλώττισης.
protocol StackProtocol {
associatedtype Element
mutating func push(_ item: Element)
mutating func pop() -> Element?
}
struct IntStack: StackProtocol {
typealias Element = Int
private var items: [Int] = []
mutating func push(_ item: Int) { items.append(item) }
mutating func pop() -> Int? { items.popLast() }
}
Το IntStack εξειδικεύει το Element ως Int, χρησιμοποιώντας typealias. Ο μεταγλωττιστής ελέγχει αν όλες οι απαιτήσεις του πρωτοκόλλου εκπληρώνονται με αυτόν τον τύπο. Χωρίς associated types θα έπρεπε να γραφτεί StackProtocol
Generic επεκτάσεις επιτρέπουν την προσθήκη μεθόδων σε έναν generic τύπο με πρόσθετους περιορισμούς. Αυτό είναι το μοτίβο “υπό όρους συμμόρφωσης”, όταν ο τύπος αποκτά λειτουργικότητα μόνο υπό ορισμένες συνθήκες. Τα subscript μπορούν επίσης να είναι generic.
Ένα subscript μπορεί να είναι γενικό: δέχεται παραμέτρους generic σε γωνιακές αγκύλες. Αυτό είναι χρήσιμο για ασφαλή πρόσβαση σε συλλογές με αυθαίρετο δείκτη, όταν ο δείκτης μπορεί να είναι διαφορετικών τύπων.
extension Array where Element: Numeric {
func sum() -> Element {
return reduce(0, +)
}
}
extension Array {
subscript<Indices: Sequence>(indices: Indices) -> [Element]
where Indices.Element == Int {
return indices.map { self[$0] }
}
}
Μια τέτοια κατασκευή παρέχει ισχυρή σύνθεση: η μέθοδος sum εμφανίζεται μόνο σε αριθμητικούς πίνακες, και το subscript με αυθαίρετους δείκτες λειτουργεί με οποιαδήποτε ακολουθία ακέραιων δεικτών. Ο μεταγλωττιστής διαχειρίζεται την ορατότητα αυτών των μελών βάσει των συνθηκών where.
Λάθη κατά τη χρήση generic συχνά σχετίζονται με εσφαλμένους περιορισμούς ή προσπάθεια χρήσης generic εκεί όπου χρειάζεται συγκεκριμένος τύπος. Ας εξετάσουμε τρία συνηθισμένα σενάρια που αντιμετωπίζουν οι προγραμματιστές.
Η προσθήκη παραμέτρων generic εκεί όπου επαρκεί ένας συγκεκριμένος τύπος — είναι αντ-πρότυπο. Αν μια συνάρτηση λειτουργεί πάντα με String, δεν χρειάζεται να γίνει
Η κλήση μεθόδου σε παράμετρο generic χωρίς να καθορίζεται ότι η μέθοδος υπάρχει — είναι λάθος.
// ❌ Error: value has no count method
func countElements<T>>(value: T) -> Int { value.count }
// ✅ Works: T: Collection guarantees count exists
func countElements<T: Collection>(value: T) -> Int { value.count }
Το λάθος είναι κατανοητό: το generic
Συχνές Ερωτήσεις
Generic — είναι ένας τρόπος να γράφεται κώδικας που λειτουργεί με οποιονδήποτε τύπο, χωρίς να αντιγράφεται η ίδια λογική για Int, String και άλλους τύπους. Αντί για συγκεκριμένο τύπο, χρησιμοποιείται ένα placeholder
Generic διατηρεί πληροφορίες για τον συγκεκριμένο τύπο στη φάση μεταγλώττισης, επιτρέποντας στον μεταγλωττιστή να ελέγχει τύπους και να παράγει βελτιστοποιημένο κώδικα. Any — είναι διαγραφή τύπου: οποιαδήποτε τιμή μπορεί να μεταδοθεί, αλλά ο τύπος χάνεται και απαιτείται μετατροπή (as?).
Χρησιμοποιήστε τη σύνταξη T: SomeProtocol σε γωνιακές αγκύλες ή μια ξεχωριστή πρόταση where T: SomeProtocol. Οι περιορισμοί εγγυώνται ότι η παράμετρος generic υποστηρίζει συγκεκριμένες μεθόδους και ιδιότητες.
Associated Type (associatedtype) — είναι ένα placeholder για τύπο εντός του πρωτοκόλλου. Το πρωτόκολλο δεν καθορίζει συγκεκριμένο τύπο, αλλά δηλώνει ένα associatedtype που εξειδικεύεται στην υλοποίηση: το Array έχει Element, το Dictionary — Key και Value.
Ο Swift μεταγλωττίζει generic κώδικα σε εξειδικευμένες εκδόσεις για κάθε συγκεκριμένο τύπο. Στην πράξη, μια generic συνάρτηση για Int δεν είναι πιο αργή από μια χειρόγραφη συνάρτηση για Int. Δεν υπάρχει επιβάρυνση για την αφαίρεση.
Σύνοψη
Θα αναπτύξουμε μια εφαρμογή για κινητά έτοιμη για χρήση
Η IT Sectr δημιουργεί εφαρμογές iOS και Android για νεοφυείς επιχειρήσεις και επιχειρήσεις από το 2017. Θα σας συμβουλεύσουμε και θα προτείνουμε την καλύτερη λύση.
Διαβάστε επίσης