Method Swizzling — een runtimetechniek waarbij de implementaties van twee methoden van een klasse tijdens de uitvoering worden omgewisseld. Het maakt het mogelijk om het gedrag van een systeelmethode te overschrijven of uit te breiden zonder een subklasse te maken en zonder de broncode te wijzigen. De techniek vond de grootste toepassing in iOS-ontwikkeling met Objective-C, maar er bestaan analogen in Kotlin/Android via reflection. Volgens NSHipster Guide by Mattt, 2024 is swizzling een van de krachtigste, maar ook gevaarlijkste mechanismen van Objective-C Runtime.
Belangrijkste
Method Swizzling — een runtimetechniek die de implementaties van twee Objective-C-methoden tijdens de uitvoering omwisselt. Na swizzling leidt het aanroepen van originalSelector tot de uitvoering van de code van swizzledSelector, en vice versa. Dit is mogelijk dankzij de architectuur van Objective-C Runtime, waar elke selector (SEL) via een dispatch table — een tabel die tijdens de uitvoering kan worden gewijzigd — is gekoppeld aan een implementatie (IMP).
De term «swizzling» werd begin jaren 2000 geïntroduceerd in de Cocoa-ontwikkelaarsgemeenschap. De techniek kreeg brede bekendheid dankzij bibliotheken: AFNetworking (swizzling van UIWebView voor het volgen van laden), Aspects (AOP-framework gebaseerd op swizzling) en FLEX (debugtool die systeemmethoden swizzlet voor inspectie). Tegenwoordig wordt swizzling in de meeste iOS-apps impliciet gebruikt — via bibliotheken voor monitoring en analyse.
Een belangrijke eigenschap van swizzling — globaliteit: de vervanging van de implementatie vindt plaats op klasseniveau, niet op exemplaarniveau. Als een bibliotheek de methode UIViewController.viewDidLoad swizzlet, beïnvloedt dit ALLE UIViewController-exemplaren in de app, inclusief systeemexemplaren. Dit is zowel de kracht van swizzling — één regel code verandert het gedrag van de hele app — als de belangrijkste bron van bugs.
Objective-C Runtime slaat in elke klasse een dispatch table op — een woordenboek waar de sleutel SEL (methodidentificator) is en de waarde IMP (pointer naar de implementatiefunctie). Wanneer de app een bericht naar een object stuurt, voert objc_msgSend een lineaire zoekopdracht uit in deze tabel. Method Swizzling vervangt de IMP van de ene SEL door de IMP van een andere SEL, waardoor aanroepen worden omgeleid.
// Implementatie van veilige method swizzling
@implementation NSObject (SafeSwizzle)
+ (void)swizzleClassMethod:(SEL)original
with:(SEL)swizzled {
Class cls = [self class];
SEL originalSel = original;
SEL swizzledSel = swizzled;
Method originalMethod = class_getInstanceMethod(cls, originalSel);
Method swizzledMethod = class_getInstanceMethod(cls, swizzledSel);
method_exchangeImplementations(originalMethod, swizzledMethod);
}
@end
De sleutelfunctie — method_exchangeImplementations(Method, Method). Deze wisselt atomair de IMP's van twee Method-objecten. Na de aanroep is de dispatch table van de klasse gewijzigd: bij toegang tot original wordt de swizzled-code uitgevoerd, bij toegang tot swizzled wordt de original-code uitgevoerd. De categorie SafeSwizzle voegt deze methode toe aan alle NSObject, waardoor elke klasse swizzling kan uitvoeren.
Een veilige implementatie van swizzling vereist het aanroepen van de originele implementatie binnen de swizzled-versie. Anders gaat het oorspronkelijke gedrag van de methode onherroepelijk verloren. Het juiste patroon — bewaar de originele IMP vóór de uitwisseling en roep deze aan in de swizzled-methode:
// Swizzling met aanroep van originele implementatie
- (void)swizzled_viewDidLoad {
// 1. Aanroep van originele implementatie
[self swizzled_viewDidLoad];
// 2. Extra logica na originele aanroep
NSLog("viewDidLoad uitgevoerd, swizzling actief");
}
+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
[self swizzleClassMethod:@selector(viewDidLoad)
with:@selector(swizzled_viewDidLoad)];
});
}
dispatch_once garandeert dat swizzling precies één keer wordt uitgevoerd tijdens de levensduur van de app. Herhaalde swizzling van dezelfde methode leidt tot oneindige recursie: de swizzled-methode roept zichzelf aan. +load wordt aangeroepen bij het laden van de klasse in de runtime — dit is een veilig punt voor swizzling, dat wordt uitgevoerd vóór de hoofdcode van de app.
Dispatch table van een Objective-C-klasse — een array van method_t-structuren die SEL, IMP en het type retourwaarde bevatten. method_exchangeImplementations wisselt eenvoudigweg twee IMP-pointers in deze tabel. Belangrijk: swizzling werkt alleen op klasseniveau, niet op protocolniveau. Als een methode is gedefinieerd in een protocol maar niet is geïmplementeerd — bevat de dispatch table geen invoer voor swizzling.
Overhead van swizzling is minimaal — het uitwisselen van twee IMP-pointers in de dispatch table duurt enkele nanoseconden. Na swizzling wordt de methodeaanroep niet vertraagd: objc_msgSend vindt de IMP in dezelfde O(1)-tijd als vóór swizzling. De enige extra bewerking — controle van de method cache bij de eerste aanroep na de uitwisseling. Volgens Apple Performance Team beïnvloedt swizzling de prestaties van de app niet.
Method Swizzling wordt gebruikt in drie hoofdscenario's: monitoring en analyse (volgen van viewDidLoad, viewDidAppear voor automatisch verzenden van gebeurtenissen), AOP-interceptie (loggen van parameters van alle methodeaanroepen) en hotfix (repareren van een bug in production zonder App Store Review via bibliotheken zoals JSPatch).
Elk van deze scenario's werkt omdat swizzling gecentraliseerd wordt toegepast. De analysebibliotheek voert swizzling eenmalig uit in +load, en alle UIViewController in de app beginnen gebeurtenissen te verzenden. De ontwikkelaar hoeft geen code aan elke controller toe te voegen — dit vermindert duplicatie en het risico op fouten.
Op Android is method swizzling in de klassieke Objective-C-zin niet mogelijk — Java/Kotlin gebruiken statische dispatchering via vtable. Er bestaan echter mechanismen die een vergelijkbaar effect bereiken: Java Reflection voor het vervangen van implementaties in runtime en Gradle Transform API / ASM voor het wijzigen van bytecode tijdens de buildfase.
// Swizzling op Android via reflection + companion object
class Logger {
companion object {
var originalImpl: (() -> Unit)? = null
}
fun log() {
println("originele log")
}
}
// Vervanging van implementatie in runtime via reflection
fun swizzleLog() {
val originalMethod = Logger::class.java
.getDeclaredMethod("log")
originalMethod.isAccessible = true
Logger.originalImpl = {
originalMethod.invoke(Logger())
}
// Vervanging via inline functie
println("swizzled: log onderschept")
}
Deze code vervangt het gedrag van de methode log() via Java Reflection: getDeclaredMethod krijgt toegang tot de privé-implementatie, isAccessible schakelt de toegangscontrole uit. In plaats van een directe aanroep van log() wordt een wrapper aangeroepen die extra logica uitvoert. Android optimaliseert echter hete methoden via JIT — reflection werkt mogelijk niet op reeds gecompileerde AOT-gedeelten.
Een betrouwbaardere benadering — bytecode manipulation via Gradle Transform API of AGP (Android Gradle Plugin) met de ASM-bibliotheek. Wijziging van bytecode wordt uitgevoerd in de compilatiefase: ASM voegt aanroepen toe aan elke methode van de klasse. Zo werken code-dekkingsinstrumenten (JaCoCo) en prestatiemonitoring (Firebase Performance Monitoring).
Method Swizzling — een techniek met hoge risico's. Conflicten tussen bibliotheken: als twee bibliotheken dezelfde methode swizzlen, is de volgorde van uitvoering niet gegarandeerd. Incompatibiliteit met iOS-updates: als Apple de handtekening wijzigt of de methode verwijdert in een nieuwe iOS-versie, leidt swizzling tot een crash. Gebrek aan zichtbaarheid in code: swizzling is niet zichtbaar in de klasse-implementatie, wat het debuggen bemoeilijkt.
| Risico | Beschrijving | Mitigatie |
|---|---|---|
| Bibliotheekconflict | Twee bibliotheken swizzlen viewDidAppear — de ene breekt de andere | Controleer of de methode al is geswizzled via class_getInstanceMethod |
| Recursie | Herhaalde swizzling van dezelfde methode veroorzaakt een oneindige lus | Gebruik altijd dispatch_once |
| Wijziging handtekening | Apple wijzigt de methodehandtekening in nieuwe iOS — IMP komt niet overeen | Test op alle ondersteunde iOS-versies |
| Onzichtbaarheid | Swizzling wordt niet weergegeven in de Xcode-aanroepstack | Documenteer alle swizzling-bewerkingen in de code |
| App Review | Apple wijst apps met ongedocumenteerde swizzling af | Gebruik alleen openbare API's en documenteer het doel |
Best practices voor veilige swizzling omvatten: roep altijd de originele implementatie aan, voer swizzling strikt uit in +load via dispatch_once, noem swizzled-methoden met een voorvoegsel (bijv. s_originalMethodName), documenteer elke swizzling-bewerking met vermelding van het doel. De bibliotheek Aspects lost het probleem van conflicten op door ketenuitvoering van blokken vóór/na de originele methode.
Alternatieven voor method swizzling hebben de voorkeur voor productiecode vanwege voorspelbaarheid en veiligheid. Delegates en protocollen (UIApplicationDelegate, UITableViewDelegate) bieden expliciete uitbreidingspunten zonder runtime-wijziging. Subclassing — het maken van een subklasse van UIViewController met overschrijving van viewDidAppear — werkt voorspelbaar en heeft geen conflicten.
SwiftUI en Combine elimineren de noodzaak voor swizzling: modifiers (onAppear, onChange) voegen gedrag declaratief toe, zonder methoden te overschrijven. In Android bereikt Jetpack Compose hetzelfde via effecten (LaunchedEffect, SideEffect) en modifiers. AOP-frameworks (AspectJ voor Android, InterposeKit voor iOS) bieden een veilig alternatief met compile-time weaving.
Volgens Apple WWDC 2024 ondersteunt Swift runtime geen method swizzling op taalniveau — @objc dynamic-methoden kunnen alleen via Objective-C Runtime worden geswizzled. Swift-apps die geen @objc gebruiken, zijn volledig beschermd tegen accidentele swizzling door bibliotheken van derden. Dit maakt Swift veiliger, maar beperkt de mogelijkheden voor runtime-instrumentatie.
SwiftUI-modifiers (onAppear, onChange, onReceive) en Jetpack Compose-effecten (LaunchedEffect, SideEffect, DisposableEffect) vervangen swizzling volledig voor UI-taken. Ze bieden een declaratieve, voorspelbare en testbare manier om cross-cutting behaviour toe te voegen zonder de dispatch table te wijzigen. In nieuwe projecten bevelen Apple en Google precies deze benadering aan in plaats van runtime-interceptie.
Veelgestelde vragen
Method Swizzling is acceptabel voor productie mits de regels worden nageleefd: dispatch_once voor eenmalige uitvoering, aanroep van de originele implementatie, testen op alle iOS-versies en documentatie. Voor eenvoudige taken kunt u beter delegates of subclassing gebruiken. Swizzling in productie is gerechtvaardigd voor bibliotheken voor monitoring en analyse.
Method Swizzling — een specifieke techniek voor het vervangen van IMP in de dispatch table. AOP (Aspect-Oriented Programming) — een paradigma waarin swizzling als een van de mechanismen kan worden gebruikt. AOP omvat ook compile-time weaving (AspectJ), proxy-gebaseerde interceptie (Spring AOP) en codegeneratie.
Gebruik een breakpoint in objc_msgSend om alle berichten te volgen. Voeg een symbolisch breakpoint toe op method_exchangeImplementations met een voorwaarde op de klassenaam. De tool FLEX laat zien welke methoden van een klasse zijn geswizzled. Gebruik voor systematische controle een lldb-script dat de dispatch table van de klasse weergeeft.
Swift ondersteunt swizzling niet op taalniveau. Method Swizzling werkt alleen voor methoden die zijn gemarkeerd met @objc dynamic en die via Objective-C Runtime worden gecompileerd. Pure Swift-methoden (zonder @objc) gebruiken statische dispatchering en kunnen niet worden geswizzled — hun dispatch table is niet beschikbaar voor wijziging.
Firebase Analytics (swizzling van viewDidAppear voor automatische schermregistratie), Amplitude, Mixpanel, FLEX (UI-inspectie), OHHTTPStubs (mocken van netwerkverzoeken), Aspects (AOP-framework). Ze voeren allemaal swizzling uit in +load via dispatch_once met aanroep van de originele implementatie.
Samenvatting
We ontwikkelen een mobiele applicatie turnkey
IT Sectr creëert sinds 2017 iOS- en Android-applicaties voor startups en bedrijven. We adviseren u en stellen de beste oplossing voor.
Lees ook