Method Swizzling — runtime texnikasıdır, bu zaman sinifin iki metodunun implementasiyaları icra zamanı yerlərini dəyişir. O, alt sinif yaratmadan və mənbə kodu dəyişdirmədən sistem metodunun davranışını ləğv etməyə və ya genişləndirməyə imkan verir. Texnika ən çox Objective-C-də iOS inkişafında tətbiq tapmışdır, lakin Kotlin/Android-də reflection vasitəsilə analoqlar mövcuddur. NSHipster Guide by Mattt, 2024-ə görə, swizzling Objective-C Runtime-ın ən güclü, lakin ən təhlükəli mexanizmlərindən biridir.
Başlıca
Method Swizzling — iki Objective-C metodunun implementasiyalarını icra zamanı yerlərini dəyişdirən runtime texnikasıdır. Swizzling-dən sonra originalSelector-ın çağırılması swizzledSelector kodunun icrasına gətirib çıxarır və əksinə. Bu, hər bir selektorun (SEL) icra zamanı dəyişdirilə bilən dispatch table vasitəsilə implementasiya (IMP) ilə əlaqələndirildiyi Objective-C Runtime arxitekturası sayəsində mümkündür.
«Swizzling» termini 2000-ci illərin əvvəllərində Cocoa tərtibatçıları cəmiyyətində təqdim edilmişdir. Texnika kitabxanalar sayəsində geniş tanınma qazanmışdır: AFNetworking (yükləməni izləmək üçün UIWebView swizzling), Aspects (swizzling-ə əsaslanan AOP çərçivəsi) və FLEX (yoxlama üçün sistem metodlarını swizzle edən debug aləti). Bu gün swizzling əksər iOS tətbiqlərində gizli şəkildə — monitoring və analitika kitabxanaları vasitəsilə istifadə olunur.
Swizzling-in vacib xüsusiyyəti — qobal olması: implementasiyanın dəyişdirilməsi instansiya deyil, sinif səviyyəsində baş verir. Kitabxana UIViewController.viewDidLoad metodunu swizzle edərsə, bu, sistem olanlar da daxil olmaqla tətbiqdəki BÜTÜN UIViewController instansiyalarına təsir edir. Bu eyni zamanda swizzling-in gücüdür — bir sətr kod bütöv tətbiqin davranışını dəyişdirir — və səhvlərin əsas mənbəyidir.
Objective-C Runtime hər sinifdə dispatch table — açarın SEL (metod identifikatoru), dəyərin IMP (implementasiya funksiyasına göstərici) olduğu lüğət saxlayır. Tətbiq obyektə mesaj göndərdikdə, objc_msgSend bu cədvəldə xətti axtarış aparır. Method Swizzling bir SEL-in IMP-sini digər SEL-in IMP-si ilə əvəz edərək çağırışları yönləndirir.
// Təhlükəsiz method swizzling-in implementasiyası
@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
əsas funksiya — method_exchangeImplementations(Method, Method). O, iki Method obyektinin IMP-sini atom şəkildə dəyişir. Çağırışdan sonra sinfin dispatch table-ı dəyişir: original-a müraciət zamanı swizzled kodu, swizzled-ə müraciət zamanı isə original kodu icra olunur. SafeSwizzle kateqoriyası bu metodu bütün NSObject-ə əlavə edərək istənilən sinfə swizzling icrasına imkan verir.
Təhlükəsiz swizzling implementasiyası swizzled versiyası daxilində orijinal implementasiyanın çağırılmasını tələb edir. Əks halda metodun orijinal davranışı geri qaytarılmaz şəkildə itir. Doğru nümunə — mübadilədən əvvəl orijinal IMP-ni saxlamaq və onu swizzled-metodda çağırmaqdır:
// Orijinal implementasiyanın çağırılması ilə swizzling
- (void)swizzled_viewDidLoad {
// 1. Orijinal implementasiyanın çağırılması
[self swizzled_viewDidLoad];
// 2. Orijinal çağırışdan sonra əlavə məntiq
NSLog("viewDidLoad yerinə yetirildi, swizzling aktivdir");
}
+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
[self swizzleClassMethod:@selector(viewDidLoad)
with:@selector(swizzled_viewDidLoad)];
});
}
dispatch_once swizzling-in tətbiqin ömrü boyu dəqiq bir dəfə yerinə yetirilməsini təmin edir. Eyni metodun təkrari swizzling-i sonsuz rekursiyaya gətirib çıxaracaq: swizzled metod özünü çağıracaqlır. +load sinif runtime-a yüklənərkən çağırılır — bu, tətbiqin əsas kodundan əvvəl icra olunan swizzling üçün təhlükəsiz nöqtədir.
Objective-C dispatch table — SEL, IMP və qaytarılan dəyər tipini ehtiva edən method_t strukturları massividir. method_exchangeImplementations sadəcə olaraq bu cədvəldə iki IMP göstəricisini dəyişir. Vacib: swizzling yalnız sinif səviyyəsində işləyir, protokol səviyyəsində yox. Metod protokolda müəyyən edilib, lakin implementasiya edilməyibsə — dispatch table swizzling üçün giriş ehtiva etmir.
Swizzling-in yükü minimaldır — dispatch table-da iki IMP göstəricisinin dəyişdirilməsi bir neçə nanosaniyə çəkir. Swizzling-dən sonra metod çağırışı yavaş mır: objc_msgSend IMP-ni swizzling-dən əvvəlki kimi eyni O(1) vaxtında tapır. Yeganə əlavə əməliyyat — mübadilədən sonra ilk çağırışda method cache yoxlanılmasıdır. Apple Performance Team məlumatlarına görə, swizzling tətbiqin performansına təsir göstərmir.
Method Swizzling üç əsas ssenaridə istifadə olunur: monitoring və analitika (avtomatik hadisə göndərilməsi üçün viewDidLoad, viewDidAppear-ın izlənməsi), AOP yaxalaması (metodun bütün çağırışlarının parametrlərinin qeydə alınması) və hotfix (JSPatch kimi kitabxanalar vasitəsilə App Store Review olmadan production-da xətanın düzəldilməsi).
Bu ssenarilərin hər biri swizzling-in mərkəzləşdirilmiş tətbiqi sayəsində işləyir. Analitika kitabxanası +load-da bir dəfə swizzling yerinə yetirir və tətbiqdəki bütün UIViewController hadisələr göndərməyə başlayır. Tərtibatçı hər kontrollerə kod əlavə etməlidir — bu, təkrarlanmanı və səhv riskini azaldır.
Android-də klassik Objective-C mənasında method swizzling mümkün deyil — Java/Kotlin vtable vasitəsilə statik dispetçerizasiyadan istifadə edir. Bununla belə, oxşar effekt əldə edən mexanizmlər mövcuddur: runtime-da implementasiyanı dəyişmək üçün Java Reflection və qurma mərhələsində baytkodu dəyişdirmək üçün Gradle Transform API / ASM.
// Android-də reflection + companion object vasitəsilə swizzling
class Logger {
companion object {
var originalImpl: (() -> Unit)? = null
}
fun log() {
println("orijinal log")
}
}
// Runtime-da reflection ilə implementasiyanın dəyişdirilməsi
fun swizzleLog() {
val originalMethod = Logger::class.java
.getDeclaredMethod("log")
originalMethod.isAccessible = true
Logger.originalImpl = {
originalMethod.invoke(Logger())
}
// Inline funksiya vasitəsilə əvəzetmə
println("swizzled: log yaxalandı")
}
Bu kod Java Reflection vasitəsilə log() metodunun davranışını dəyişir: getDeclaredMethod özəl implementasiyaya giriş əldə edir, isAccessible giriş yoxlanılmasını söndürür. Birbaşa log() çağırışı əvəzinə əlavə məntiq yerinə yetirən sarıcı çağırılır. Lakin Android isti metodları JIT vasitəsilə optimallaşdırır — reflection artıq kompilyasiya edilmiş AOT hissələrində işləməyə bilər.
Daha etibarlı yanaşma — ASM kitabxanası ilə Gradle Transform API və ya AGP (Android Gradle Plugin) vasitəsilə bytecode manipulation. Baytkodun dəyişdirilməsi kompilyasiya mərhələsində yerinə yetirilir: ASM sinifin hər metoduna çağırışlar əlavə edir. Kod əhatə alətləri (JaCoCo) və performans monitorinqi (Firebase Performance Monitoring) belə işləyir.
Method Swizzling — yüksək riskli texnikadır. Kitabxanalar arasında münaqişələr: iki kitabxana eyni metodu swizzle edərsə, icra sırası təminat vermir. iOS yeniləmələri ilə uyğunsuzluq: Apple yeni iOS versiyasında imzanı dəyişir və ya metodu silirsə, swizzling çöküşə gətirib çıxarır. Kodda görünməmə: swizzling sinif implementasiyasında görünmür, bu da debug prosesini çətinləşdirir.
| Risk | Təsvir | Azaldılması |
|---|---|---|
| Kitabxana münaqişəsi | İki kitabxana viewDidAppear-ı swizzle edir — biri digərini pozur | Metodun artıq swizzle olunub-olunmadığını class_getInstanceMethod ilə yoxlayın |
| Rekursiya | Eyni metodun təkrari swizzling-i sonsuz dövrə yaradır | Həmişə dispatch_once istifadə edin |
| İmza dəyişikliyi | Apple yeni iOS-da metodun imzasını dəyişir — IMP uyğunsuz gəlir | Bütün dəstəklənən iOS versiyalarında test edin |
| Görünməmə | Swizzling Xcode çağırış stack-ində göstərilmir | Bütün swizzling əməliyyatlarını kodda sənədləşdirin |
| App Review | Apple sənədləşdirilməmiş swizzling ilə tətbiqləri rədd edir | Yalnız public API-lərdən istifadə edin və məqsədi sənədləşdirin |
Best practices təhlükəsiz swizzling üçün aşağıdakıları əhatə edir: həmişə orijinal implementasiyanı çağırmaq, swizzling-i ciddi şəkildə +load-da dispatch_once vasitəsilə yerinə yetirmək, swizzled-metodları prefiks ilə adlandırmaq (məs. s_originalMethodName), hər swizzling əməliyyatını məqsədi göstərərək sənədləşdirmək. Aspects kitabxanası münaqişələr problemini orijinal metoddan əvvəl/sonra blokların zəncirli icrası ilə həll edir.
Method swizzling-ə alternativlər proqnozlaşdırıla bilmə və təhlükəsizlik səbəbindən production kodu üçün üstünlük təşkil edir. Deleqatlar və protokollar (UIApplicationDelegate, UITableViewDelegate) runtime dəyişdirilmədən açıq genişlənmə nöqtələri təmin edir. Alt siniflər — viewDidAppear-ı ləğv etməklə UIViewController alt sinfinin yaradılması — proqnozlaşdırıla bilən şəkildə işləyir və münaqişələr yaratmır.
SwiftUI və Combine swizzling ehtiyacını aradan qaldırır: modifikatorlar (onAppear, onChange) metodları ləğv etmədən deklarativ şəkildə davranış əlavə edir. Android Jetpack Compose-da eyni effektə effektlər (LaunchedEffect, SideEffect) və modifikatorlar vasitəsilə nail olunur. AOP çərçivələri (Android üçün AspectJ, iOS üçün InterposeKit) compile-time weaving ilə təhlükəsiz alternativ təmin edir.
Apple WWDC 2024 məlumatlarına görə, Swift runtime dil səviyyəsində method swizzling-i dəstəkləmir — @objc dynamic metodlar yalnız Objective-C Runtime vasitəsilə swizzle edilə bilər. @objc istifadə etməyən Swift tətbiqləri üçüncü tərəf kitabxanalar tərəfindən təsadüfi swizzling-dən tam qorunur. Bu, Swift-i daha təhlükəsiz edir, lakin runtime instrumentasiya imkanlarını məhdudlaşdırır.
SwiftUI modifikatorları (onAppear, onChange, onReceive) və Jetpack Compose effektləri (LaunchedEffect, SideEffect, DisposableEffect) UI tapşırıqları üçün swizzling-i tamamilə əvəz edir. Onlar dispatch table dəyişdirilmədən kəsişən davranış əlavə etmək üçün deklarativ, proqnozlaşdırıla bilən və yoxlanıla bilən üsul təmin edir. Yeni layihələrdə Apple və Google runtime yaxalaması əvəzinə məhz bu yanaşmanı tövsiyə edir.
Tez-tez verilən suallar
Method Swizzling qaydalara əməl edildikdə production üçün məqbuldur: birdəfəlik icra üçün dispatch_once, orijinal implementasiyanın çağırılması, bütün iOS versiyalarında test və sənədləşdirmə. Sadə tapşırıqlar üçün deleqatlar və ya alt siniflərdən istifadə etmək daha yaxşıdır. Production-da Swizzling monitoring və analitika kitabxanaları üçün əsaslandırılır.
Method Swizzling — dispatch table-da IMP-nin dəyişdirilməsinin konkret texnikasıdır. AOP (Aspect-Oriented Programming) — swizzling-in mexanizmlərdən biri kimi istifadə oluna biləcəyi paradiqmadır. AOP həmçinin compile-time weaving (AspectJ), proxy-əsaslı yaxalama (Spring AOP) və code generation-ı əhatə edir.
Bütün mesajları izləmək üçün objc_msgSend-də breakpoint istifadə edin. Sinif adı şərti ilə method_exchangeImplementations üzrə simvolik breakpoint əlavə edin. FLEX aləti sinfin hansı metodlarının swizzle edildiyini göstərir. Sistematik yoxlama üçün sinfin dispatch table-ını göstərən lldb skriptindən istifadə edin.
Swift dil səviyyəsində swizzling-i dəstəkləmir. Method Swizzling yalnız Objective-C Runtime vasitəsilə kompilyasiya olunan @objc dynamic ilə qeyd edilmiş metodlar üçün işləyir. Təmiz Swift metodları (@objc olmadan) statik dispetçerizasiyadan istifadə edir və swizzle edilə bilməz — onların dispatch table-ı dəyişdirilmə üçün əlçatan deyil.
Firebase Analytics (avtomatik ekran izlənməsi üçün viewDidAppear swizzling), Amplitude, Mixpanel, FLEX (UI yoxlaması), OHHTTPStubs (şəbək sorğularının mock edilməsi), Aspects (AOP çərçivəsi). Hamısı orijinal implementasiyanın çağırılması ilə dispatch_once vasitəsilə +load-da swizzling yerinə yetirir.
Nəticələr
Açar təslim mobil tətbiq hazırlayacağıq
IT Sectr 2017-ci ildən startaplar və bizneslər üçün iOS və Android tətbiqləri yaradır. Sizə məsləhət verəcəyik və ən yaxşı həlli təklif edəcəyik.
Həm də oxuyun