Method Swizzling, iki sınıf metodunun uygulamalarının çalışma zamanında yer değiştirdiği bir runtime tekniğidir. Bir alt sınıf oluşturmadan veya kaynak kodu değiştirmeden sistem metodunun davranışını geçersiz kılmayı veya tamamlamayı sağlar. Bu teknik en büyük uygulamasını Objective-C ile iOS geliştirmede bulmuştur, ancak Kotlin/Android'de yansıma yoluyla benzerleri mevcuttur. NSHipster Guide by Mattt, 2024'e göre, swizzling Objective-C Runtime'ın en güçlü ama aynı zamanda en tehlikeli mekanizmalarından biridir.
Ana Noktalar
Method Swizzling, iki Objective-C metodunun uygulamalarını değiştiren bir runtime tekniğidir. Swizzling'den sonra, originalSelector'ı çağırmak swizzledSelector'ın kodunu çalıştırır ve bunun tersi de geçerlidir. Bu, her seçicinin (SEL) bir dispatch tablosu aracılığıyla bir uygulamaya (IMP) bağlı olduğu Objective-C Runtime mimarisi sayesinde mümkündür — çalışma zamanında değiştirilebilen bir tablo.
“swizzling” terimi, 2000'lerin başında Cocoa geliştirici topluluğunda tanıtılmıştır. Teknik, şu kütüphaneler sayesinde geniş çapta tanınmıştır: AFNetworking (yüklemeyi izlemek için UIWebView swizzling'i), Aspects (swizzling tabanlı AOP çerçevesi) ve FLEX (inceleme için sistem metotlarını swizzle eden hata ayıklama aracı). Günümüzde swizzling, çoğu iOS uygulamasında örtülü olarak — izleme ve analiz kütüphaneleri aracılığıyla — kullanılmaktadır.
Swizzling'in önemli bir özelliği küreselliktir: uygulama değişimi örnek düzeyinde değil, sınıf düzeyinde gerçekleşir. Bir kütüphane UIViewController.viewDidLoad metodunu swizzle ederse, bu sisteminkiler de dahil olmak üzere uygulamadaki TÜM UIViewController örneklerini etkiler. Bu, swizzling'in gücü — tek bir kod satırı tüm uygulamanın davranışını değiştirir — ve aynı zamanda hataların ana kaynağıdır.
Objective-C Runtime, her sınıfta bir dispatch tablosu saklar — anahtarı SEL (metot tanımlayıcısı) ve değeri IMP (uygulama fonksiyonunun işaretçisi) olan bir sözlük. Bir uygulama bir nesneye mesaj gönderdiğinde, objc_msgSend bu tabloda doğrusal bir arama yapar. Method Swizzling, bir SEL'in IMP'sini başka bir SEL'in IMP'siyle değiştirerek çağrıları yönlendirir.
// Güvenli method swizzling uygulaması
@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
Anahtar işlev method_exchangeImplementations(Method, Method)'dur. İki Method nesnesinin IMP'lerini atomik olarak değiştirir. Çağrıdan sonra, sınıfın dispatch tablosu değiştirilir: original çağrıldığında swizzled kodu, swizzled çağrıldığında original kodu çalıştırılır. SafeSwizzle kategorisi bu metodu tüm NSObject'lere ekleyerek herhangi bir sınıfın swizzling yapmasına olanak tanır.
Güvenli bir swizzling uygulaması, swizzled sürümün içinde orijinal uygulamayı çağırmayı gerektirir. Aksi takdirde, metodun orijinal davranışı geri döndürülemez şekilde kaybolur. Doğru desen, değişimden önce orijinal IMP'yi kaydetmek ve swizzled metot içinde çağırmaktır:
// Orijinal uygulama çağrısı ile Swizzling
- (void)swizzled_viewDidLoad {
// 1. Orijinal uygulamayı çağırma
[self swizzled_viewDidLoad];
// 2. Orijinal çağrıdan sonra ek mantık
NSLog("viewDidLoad çalıştırıldı, swizzling aktif");
}
+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
[self swizzleClassMethod:@selector(viewDidLoad)
with:@selector(swizzled_viewDidLoad)];
});
}
dispatch_once, swizzling'in uygulamanın ömrü boyunca tam olarak bir kez çalıştırılmasını garanti eder. Aynı metodu yeniden swizzle'lamak sonsuz özyinelemeye yol açar: swizzled metot kendisini çağırır. +load, sınıf runtime'a yüklendiğinde çağrılır — bu, ana uygulama kodundan önce çalışan swizzling için güvenli bir noktadır.
Bir Objective-C sınıfının dispatch tablosu, SEL, IMP ve dönüş türünü içeren method_t yapılarının bir dizisidir. method_exchangeImplementations basitçe bu tablodaki iki IMP işaretçisini değiştirir. Önemli: swizzling yalnızca sınıf düzeyinde çalışır, protokol düzeyinde çalışmaz. Bir metot protokolde tanımlanmış ancak uygulanmamışsa, dispatch tablosu swizzling için bir giriş içermez.
Swizzling ek yükü minimumdur — dispatch tablosunda iki IMP işaretçisini değiştirmek birkaç nanosaniye sürer. Swizzling'den sonra, metot dağıtımı yavaşlamaz: objc_msgSend, swizzling'den öncekiyle aynı O(1) sürede IMP'yi bulur. Tek ek işlem, değişimden sonraki ilk çağrıda metot önbellek kontrolüdür. Apple Performance Team verilerine göre, swizzling uygulama performansını etkilemez.
Method Swizzling üç ana senaryoda kullanılır: izleme ve analiz (otomatik etkinlik gönderimi için viewDidLoad, viewDidAppear takibi), AOP müdahalesi (tüm metot çağrılarının parametrelerinin günlüğe kaydedilmesi) ve hotfix (JSPatch gibi kütüphaneler aracılığıyla App Store Review olmadan üretim hatasının düzeltilmesi).
Bu senaryoların her biri, swizzling'in merkezi olarak uygulanması sayesinde çalışır. Bir analiz kütüphanesi +load'da bir kez swizzling yapar ve uygulamadaki tüm UIViewController örnekleri etkinlik göndermeye başlar. Geliştiricinin her denetleyiciye kod eklemesi gerekmez — bu, tekrarı ve hata riskini azaltır.
Android'de, klasik Objective-C anlamında method swizzling mümkün değildir — Java/Kotlin, vtable aracılığıyla statik dağıtım kullanır. Ancak, benzer bir etki elde eden mekanizmalar mevcuttur: çalışma zamanında uygulama değişimi için Java Yansıması ve derleme zamanında bayt kodu değişikliği için Gradle Transform API / ASM.
// Android'de yansıma + companion object aracılığıyla Swizzling
class Logger {
companion object {
var originalImpl: (() -> Unit)? = null
}
fun log() {
println("orijinal günlük")
}
}
// Runtime'da yansıma yoluyla uygulama değişimi
fun swizzleLog() {
val originalMethod = Logger::class.java
.getDeclaredMethod("log")
originalMethod.isAccessible = true
Logger.originalImpl = {
originalMethod.invoke(Logger())
}
// Satır içi işlev aracılığıyla değiştirme
println("swizzled: günlük ele geçirildi")
}
Bu kod, Java Yansıması aracılığıyla log() metodunun davranışını değiştirir: getDeclaredMethod özel uygulamaya erişir, isAccessible erişim kontrollerini devre dışı bırakır. Doğrudan log() çağırmak yerine, ek mantık yürüten bir sarmalayıcı çağrılır. Ancak Android, JIT aracılığıyla sıcak metotları optimize eder — yansıma, önceden derlenmiş AOT segmentlerinde çalışmayabilir.
Daha güvenilir bir yaklaşım, ASM kütüphanesi ile Gradle Transform API veya AGP (Android Gradle Plugin) aracılığıyla bayt kodu manipülasyonudur. Bayt kodu değişikliği derleme zamanında gerçekleştirilir: ASM, sınıfın her metoduna çağrı ekler. Kod kapsamı araçları (JaCoCo) ve performans izleme araçları (Firebase Performance Monitoring) bu şekilde çalışır.
Method Swizzling yüksek riskli bir tekniktir. Kütüphaneler arasında çakışmalar: iki kütüphane aynı metodu swizzle ederse, yürütme sırası garanti edilmez. iOS güncellemeleriyle uyumsuzluk: Apple yeni bir iOS sürümünde imzayı değiştirir veya bir metodu kaldırırsa, swizzling çökmelere yol açar. Kodda görünürlük eksikliği: swizzling sınıf uygulamasında görünmez, bu da hata ayıklamayı zorlaştırır.
| Risk | Açıklama | Azaltma |
|---|---|---|
| Kütüphane çakışması | İki kütüphane viewDidAppear'ı swizzler — biri diğerini bozar | class_getInstanceMethod ile metodun zaten swizzlenmiş olup olmadığını kontrol edin |
| Özyineleme | Aynı metodu yeniden swizzle'lamak sonsuz döngüye neden olur | Her zaman dispatch_once kullanın |
| İmza değişikliği | Apple yeni iOS'te metot imzasını değiştirir — IMP uyuşmazlığı | Desteklenen tüm iOS sürümlerinde test edin |
| Görünmezlik | Swizzling Xcode çağrı yığınında görünmez | Tüm swizzling işlemlerini kodda belgeleyin |
| App Review | Apple belgelenmemiş swizzling içeren uygulamaları reddeder | Yalnızca genel API'leri kullanın ve amacı belgeleyin |
Güvenli swizzling için en iyi uygulamalar şunları içerir: her zaman orijinal uygulamayı çağırın, swizzling'i dispatch_once aracılığıyla +load'da kesinlikle gerçekleştirin, swizzlenmiş metotları bir ön ekle adlandırın (örneğin, s_originalMethodName), her swizzling işlemini amacıyla birlikte belgeleyin. Aspects kütüphanesi, orijinal metottan önce/sonra blokların zincirleme yürütülmesi yoluyla çakışma sorununu çözer.
Method swizzling alternatifleri, öngörülebilirlik ve güvenlik nedeniyle üretim kodu için tercih edilir. Temsilciler ve protokoller (UIApplicationDelegate, UITableViewDelegate) runtime'ı değiştirmeden açık genişleme noktaları sağlar. Alt sınıf oluşturma — viewDidAppear'ı geçersiz kılan bir UIViewController alt sınıfı oluşturma — öngörülebilir şekilde çalışır ve çakışmaları yoktur.
SwiftUI ve Combine, swizzling ihtiyacını ortadan kaldırır: değiştiriciler (onAppear, onChange) metotları geçersiz kılmadan bildirimsel olarak davranış ekler. Android Jetpack Compose'da aynı şey etkiler (LaunchedEffect, SideEffect) ve değiştiriciler aracılığıyla elde edilir. AOP çerçeveleri (Android için AspectJ, iOS için InterposeKit) derleme zamanı örgüsü ile güvenli bir alternatif sunar.
Apple WWDC 2024 verilerine göre, Swift runtime dil düzeyinde method swizzling'i desteklemez — @objc dynamic metotlar yalnızca Objective-C Runtime aracılığıyla swizzlenebilir. @objc kullanmayan Swift uygulamaları, üçüncü taraf kütüphaneler tarafından yanlışlıkla yapılan swizzling'den tamamen korunur. Bu, Swift'i daha güvenli kılar ancak runtime enstrümantasyon yeteneklerini sınırlar.
SwiftUI değiştiricileri (onAppear, onChange, onReceive) ve Jetpack Compose etkileri (LaunchedEffect, SideEffect, DisposableEffect), UI görevleri için swizzling'in yerini tamamen alır. Dispatch tablosunu değiştirmeden kesişen davranış eklemek için bildirimsel, öngörülebilir ve test edilebilir bir yol sağlarlar. Yeni projelerde Apple ve Google, runtime müdahalesi yerine bu yaklaşımı önermektedir.
Sıkça Sorulan Sorular
Method Swizzling, kurallara uyulduğunda üretim için kabul edilebilir: tek seferlik çalıştırma için dispatch_once, orijinal uygulamayı çağırma, tüm iOS sürümlerinde test etme ve belgeleme. Basit görevler için temsilciler veya alt sınıf oluşturma kullanmak daha iyidir. Üretimde Swizzling, izleme ve analiz kütüphaneleri için haklıdır.
Method Swizzling, dispatch tablosunda IMP'leri değiştirmek için belirli bir tekniktir. AOP (Yönelim Odaklı Programlama), swizzling'in mekanizmalardan biri olarak kullanılabileceği bir paradigmadır. AOP ayrıca derleme zamanı örgüsü (AspectJ), proxy tabanlı müdahale (Spring AOP) ve kod oluşturmayı da içerir.
Tüm mesajları izlemek için objc_msgSend'te bir kesme noktası kullanın. Sınıf adına koşullu olarak method_exchangeImplementations üzerinde sembolik bir kesme noktası ekleyin. FLEX aracı, sınıfın hangi metotlarının swizzlenmiş olduğunu gösterir. Sistematik kontrol için, sınıfın dispatch tablosunu çıkaran bir lldb betiği kullanın.
Swift, dil düzeyinde swizzling'i desteklemez. Method Swizzling yalnızca @objc dynamic ile işaretlenmiş metotlar için çalışır ve bunlar Objective-C Runtime aracılığıyla derlenir. Saf Swift metotları (@objc olmadan) statik dağıtım kullanır ve swizzlenemez — dispatch tabloları değişiklik için erişilebilir değildir.
Firebase Analytics (otomatik ekran izleme için viewDidAppear swizzling'i), Amplitude, Mixpanel, FLEX (UI incelemesi), OHHTTPStubs (ağ isteği simülasyonu), Aspects (AOP çerçevesi). Hepsi, orijinal uygulamayı çağırarak dispatch_once aracılığıyla +load'da swizzling gerçekleştirir.
Özet
Anahtar teslim bir mobil uygulama geliştireceğiz
IT Sectr, 2017'den beri girişimler ve işletmeler için iOS ve Android uygulamaları oluşturmaktadır. Size danışmanlık yapacak ve en iyi çözümü önereceğiz.
Ayrıca okuyun