Generic (generisk programmering) — är en Swift-mekanism som gör det möjligt att skriva typer och funktioner med en parameteriserad typ
Huvudpunkter
Generic (generisk typ) — är en syntaktisk konstruktion i Swift som möjliggör skrivning av flexibel, återanvändbar kod med typparametrisering. Istället för en konkret typ skrivs en placeholder i vinkelparenteser — vanligtvis
Utan generics skulle separata funktioner behöva skrivas för varje typ: swapInts, swapStrings, swapDates. Den generiska funktionen swap ersätter alla tre varianter med en enda deklaration. Detta minskar duplicering, ökar läsbarheten och minskar sannolikheten för fel vid kopiering av kod.
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-kompilatorn genererar specialiserad kod för varje konkret användning av generics. Detta innebär att den generiska funktionen swapValues
Generiska funktioner — är funktioner som accepterar en eller flera typ-parametrar. Typ-parametrarna anges efter funktionsnamnet i vinkelparenteser:
En funktion kan ha flera generiska parametrar. Till exempel accepterar funktionen 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
}
Begränsningarna K: Hashable och where V: Equatable garanterar att findKey endast kan anropas med en ordbok vars nycklar är hashbara och värden är jämförbara. Sådana begränsningar är inte byråkrati utan nödvändighet: utan Hashable kan sökning efter nyckel inte indexeras, utan Equatable kan value inte jämföras med dictValue.
where — är ett nyckelord i Swift som lägger ytterligare begränsningar på generiska parametrar. Till skillnad från enkel specificering av protokoll i vinkelparenteser (
Where-satsen skrivs efter signaturen för funktionen eller typen, före den öppnande klammerparentesen. I where kan man specificera att T: Comparable & Hashable (samtidigt två protokoll), eller att T.U == Int (konkretisering av 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
}
}
Extensionen med where lägger till metoder endast för de typer som uppfyller villkoret. Container
Associated Types (associerade typer) — är ett sätt att göra protokollet generiskt utan att specificera den konkreta typen i deklarationsfasen. Istället för
Med hjälp av where kan kopplingar upprättas mellan associated types från olika protokoll. Till exempel kan man kräva att Item från ett protokoll matchar Iterator.Element från ett annat. Detta garanterar typkompatibilitet på kompileringsnivå.
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 konkretiserar Element som Int med hjälp av typealias. Kompilatorn kontrollerar att alla protokollkrav uppfylls med denna typ. Utan associated types skulle man behöva skriva StackProtocol
Generiska extensions gör det möjligt att lägga till metoder till en generisk typ med ytterligare begränsningar. Detta är mönstret “villkorlig överensstämmelse”, där typen får funktionalitet endast under vissa förhållanden. Subscripts kan också vara generiska.
Ett subscript kan vara generiskt: det accepterar generiska parametrar i vinkelparenteser. Detta är användbart för säker åtkomst till samlingar med ett godtyckligt index, när indexet kan vara av olika typer.
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] }
}
}
En sådan konstruktion ger kraftfull komposition: metoden sum visas endast i numeriska arrayer, och subscriptet med godtyckliga index fungerar med vilken sekvens av heltalsindex som helst. Kompilatorn hanterar själv synligheten för dessa medlemmar baserat på where-villkor.
Misstag vid användning av generics är ofta relaterade till felaktiga begränsningar eller försök att använda generics där en konkret typ behövs. Låt oss titta på tre vanliga scenarier som utvecklare stöter på.
Att lägga till generiska parametrar där en konkret typ räcker — är ett antimönster. Om en funktion alltid arbetar med String, behöver man inte göra den till
Att anropa en metod på en generisk parameter utan att specificera att metoden finns — är ett fel.
// ❌ 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 }
Felet är förståeligt: generisk
Vanliga frågor
Generic — är ett sätt att skriva kod som fungerar med vilken typ som helst, utan att duplicera samma logik för Int, String och andra typer. Istället för en konkret typ används en placeholder
Generic bevarar information om den konkreta typen i kompileringsfasen, vilket gör att kompilatorn kan kontrollera typer och generera optimerad kod. Any — är typradering: vilket värde som helst kan skickas, men typen går förlorad och konvertering (as?) krävs.
Använd syntaxen T: SomeProtocol i vinkelparenteser eller en separat sats where T: SomeProtocol. Begränsningar garanterar att den generiska parametern stöder vissa metoder och egenskaper.
Associated Type (associatedtype) — är en placeholder för en typ inuti protokollet. Protokollet specificerar inte en konkret typ utan deklarerar en associatedtype som konkretiseras i implementeringen: Array har Element, Dictionary — Key och Value.
Swift kompilerar generisk kod till specialiserade versioner för varje konkret typ. I praktiken är en generisk funktion för Int inte långsammare än en handskriven funktion för Int. Det finns ingen overhead för abstraktion.
Sammanfattning
Vi utvecklar en mobil applikation nyckelfärdigt
IT Sectr skapar iOS- och Android-applikationer för startups och företag sedan 2017. Vi ger dig råd och föreslår den bästa lösningen.
Läs också