Method Swizzling ایک رن ٹائم تکنیک ہے جس میں دو کلاس طریقوں کے نفاذ کو عملدرآمد کے دوران آپس میں تبدیل کیا جاتا ہے۔ یہ سب کلاس بنائے یا سورس کوڈ میں تبدیلی کیے بغیر سسٹم طریقہ کار کے رویے کو اوور رائڈ یا مکمل کرنے کی اجازت دیتا ہے۔ اس تکنیک کا سب سے زیادہ استعمال iOS ڈیولپمنٹ میں Objective-C کے ساتھ ہوا ہے، لیکن Kotlin/Android میں ریفلیکشن کے ذریعے مشابہ طریقے موجود ہیں۔ NSHipster Guide by Mattt, 2024 کے مطابق، swizzling Objective-C Runtime کے سب سے طاقتور لیکن سب سے خطرناک میکانزم میں سے ایک ہے۔
اہم نکات
Method Swizzling ایک رن ٹائم تکنیک ہے جو دو Objective-C طریقوں کے نفاذ کو آپس میں تبدیل کرتی ہے۔ Swizzling کے بعد، originalSelector کو کال کرنے سے swizzledSelector کا کوڈ عملدرآمد ہوتا ہے، اور اس کے برعکس بھی۔ یہ Objective-C Runtime کے فن تعمیر کی بدولت ممکن ہے، جہاں ہر سلیکٹر (SEL) ایک ڈسپیچ ٹیبل کے ذریعے ایک نفاذ (IMP) سے منسلک ہوتا ہے — ایک جدول جسے عملدرآمد کے دوران تبدیل کیا جا سکتا ہے۔
“swizzling” کی اصطلاح 2000 کی دہائی کے اوائل میں Cocoa ڈیولپر کمیونٹی میں متعارف کرائی گئی تھی۔ اس تکنیک کو لائبریریوں جیسے: AFNetworking (لوڈنگ ٹریک کرنے کے لیے UIWebView کا swizzling)، Aspects (swizzling پر مبنی AOP فریم ورک) اور FLEX (معائنہ کے لیے سسٹم طریقوں کو swizzle کرنے والا ڈیبگنگ ٹول) کی بدولت وسیع پہچان ملی۔ آج، swizzling زیادہ تر iOS ایپلیکیشنز میں بلاواسطہ استعمال ہوتا ہے — مانیٹرنگ اور اینالیٹکس لائبریریوں کے ذریعے۔
Swizzling کی ایک اہم خصوصیت عالمگیریت ہے: نفاذ کی تبدیلی انسٹینس سطح پر نہیں، بلکہ کلاس سطح پر ہوتی ہے۔ اگر کوئی لائبریری UIViewController.viewDidLoad طریقہ کو swizzle کرتی ہے، تو یہ ایپلیکیشن میں UIViewController کے تمام انسٹینسز کو متاثر کرتی ہے، بشمول سسٹم والے۔ یہ swizzling کی طاقت ہے — کوڈ کی ایک لائن پوری ایپلیکیشن کے رویے کو تبدیل کر دیتی ہے — اور بگز کا بنیادی ذریعہ بھی۔
Objective-C Runtime ہر کلاس میں ایک ڈسپیچ ٹیبل محفوظ کرتا ہے — ایک لغت جہاں کلید SEL (طریقہ شناخت کنندہ) ہے اور قدر IMP (نفاذ فنکشن کا پوائنٹر) ہے۔ جب کوئی ایپلیکیشن کسی آبجیکٹ کو پیغام بھیجتی ہے، تو objc_msgSend اس ٹیبل میں لکیری تلاش کرتا ہے۔ Method Swizzling ایک SEL کے IMP کو دوسرے SEL کے IMP سے بدل دیتا ہے، کالز کو ری ڈائریکٹ کرتا ہے۔
// محفوظ 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
اہم فنکشن method_exchangeImplementations(Method, Method) ہے۔ یہ دو Method آبجیکٹس کے IMPs کو ایٹمی طور پر تبدیل کرتا ہے۔ کال کے بعد، کلاس کا ڈسپیچ ٹیبل تبدیل ہو جاتا ہے: original کو کال کرنے سے swizzled کوڈ، swizzled کو کال کرنے سے original کوڈ عملدرآمد ہوتا ہے۔ SafeSwizzle زمرہ اس طریقہ کو تمام NSObject میں شامل کرتا ہے، جس سے کوئی بھی کلاس swizzling کر سکتی ہے۔
محفوظ swizzling نفاذ کے لیے swizzled ورژن کے اندر اصل نفاذ کو کال کرنا ضروری ہے۔ ورنہ، طریقہ کا اصل رویہ مستقل طور پر کھو جاتا ہے۔ صحیح نمونہ یہ ہے کہ تبادلے سے پہلے اصل IMP کو محفوظ کریں اور swizzled طریقہ میں اسے کال کریں:
// اصل نفاذ کال کے ساتھ Swizzling
- (void)swizzled_viewDidLoad {
// 1. اصل نفاذ کو کال کرنا
[self swizzled_viewDidLoad];
// 2. اصل کال کے بعد اضافی منطق
NSLog("viewDidLoad عملدرآمد ہوا، swizzling فعال");
}
+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
[self swizzleClassMethod:@selector(viewDidLoad)
with:@selector(swizzled_viewDidLoad)];
});
}
dispatch_once اس بات کی ضمانت دیتا ہے کہ swizzling ایپلیکیشن کی زندگی میں بالکل ایک بار عملدرآمد ہوتا ہے۔ اسی طریقہ کو دوبارہ swizzle کرنے سے لامحدود تکرار ہوگی: swizzled طریقہ خود کو کال کرے گا۔ +load کو کلاس کے رن ٹائم میں لوڈ ہونے پر کال کیا جاتا ہے — یہ swizzling کے لیے ایک محفوظ نقطہ ہے جو مرکزی ایپلیکیشن کوڈ سے پہلے عملدرآمد ہوتا ہے۔
Objective-C کلاس کا ڈسپیچ ٹیبل method_t ڈھانچوں کی ایک صف ہے جس میں SEL، IMP اور واپسی کی قسم ہوتی ہے۔ method_exchangeImplementations صرف اس ٹیبل میں دو IMP پوائنٹرز کو تبدیل کرتا ہے۔ اہم: swizzling صرف کلاس سطح پر کام کرتا ہے، پروٹوکول سطح پر نہیں۔ اگر کوئی طریقہ پروٹوکول میں متعین ہے لیکن نافذ نہیں ہے، تو ڈسپیچ ٹیبل میں swizzling کے لیے کوئی اندراج نہیں ہوتا۔
Swizzling کا اضافی بوجھ کم سے کم ہے — ڈسپیچ ٹیبل میں دو IMP پوائنٹرز کو تبدیل کرنے میں چند نینو سیکنڈ لگتے ہیں۔ Swizzling کے بعد، طریقہ کی تقسیم سست نہیں ہوتی: objc_msgSend swizzling سے پہلے کی طرح اسی O(1) وقت میں IMP ڈھونڈتا ہے۔ واحد اضافی عمل تبادلے کے بعد پہلی کال پر طریقہ کیش چیک ہے۔ Apple Performance Team کے اعداد و شمار کے مطابق، swizzling ایپلیکیشن کی کارکردگی کو متاثر نہیں کرتا۔
Method Swizzling تین اہم منظرناموں میں استعمال ہوتا ہے: مانیٹرنگ اور اینالیٹکس (خودکار ایونٹ بھیجنے کے لیے viewDidLoad، viewDidAppear کی ٹریکنگ)، AOP مداخلت (تمام طریقہ کالز کے پیرامیٹرز لاگ کرنا)، اور ہاٹ فکس (JSPatch جیسی لائبریریوں کے ذریعے App Store Review کے بغیر پروڈکشن بگ کو ٹھیک کرنا)۔
ان میں سے ہر منظرنامہ اس لیے کام کرتا ہے کیونکہ swizzling مرکزی طور پر لاگو کیا جاتا ہے۔ ایک اینالیٹکس لائبریری +load میں ایک بار swizzling کرتی ہے، اور ایپلیکیشن میں UIViewController کے تمام انسٹینسز ایونٹس بھیجنا شروع کر دیتے ہیں۔ ڈیولپر کو ہر کنٹرولر میں کوڈ شامل کرنے کی ضرورت نہیں — اس سے تکرار اور خرابیوں کا خطرہ کم ہوتا ہے۔
Android پر، کلاسک Objective-C معنوں میں method swizzling ناممکن ہے — Java/Kotlin vtable کے ذریعے جامد تقسیم استعمال کرتے ہیں۔ تاہم، ایسے میکانزم موجود ہیں جو ایک جیسا اثر حاصل کرتے ہیں: رن ٹائم پر نفاذ کی تبدیلی کے لیے Java ریفلیکشن اور تعمیر کے وقت بائٹ کوڈ تبدیلی کے لیے Gradle Transform API / ASM۔
// Android پر ریفلیکشن + companion object کے ذریعے Swizzling
class Logger {
companion object {
var originalImpl: (() -> Unit)? = null
}
fun log() {
println("اصل لاگ")
}
}
// ریفلیکشن کے ذریعے رن ٹائم پر نفاذ کی تبدیلی
fun swizzleLog() {
val originalMethod = Logger::class.java
.getDeclaredMethod("log")
originalMethod.isAccessible = true
Logger.originalImpl = {
originalMethod.invoke(Logger())
}
// ان لائن فنکشن کے ذریعے تبدیلی
println("swizzled: لاگ روک لیا گیا")
}
یہ کوڈ Java ریفلیکشن کے ذریعے log() طریقہ کے رویے کو بدلتا ہے: getDeclaredMethod نجی نفاذ تک رسائی حاصل کرتا ہے، isAccessible رسائی کی جانچ کو غیر فعال کرتا ہے۔ براہ راست log() کال کرنے کے بجائے، ایک ریپر کال کیا جاتا ہے جو اضافی منطق چلاتا ہے۔ تاہم، Android JIT کے ذریعے گرم طریقوں کو بہتر بناتا ہے — ریفلیکشن پہلے سے مرتب کردہ AOT حصوں پر کام نہیں کر سکتا۔
ایک زیادہ قابل اعتماد طریقہ ASM لائبریری کے ساتھ Gradle Transform API یا AGP (Android Gradle Plugin) کے ذریعے بائٹ کوڈ ہیرا پھیری ہے۔ بائٹ کوڈ کی تبدیلی تعمیر کے وقت کی جاتی ہے: ASM کلاس کے ہر طریقہ میں کالز شامل کرتا ہے۔ کوڈ کوریج ٹولز (JaCoCo) اور کارکردگی کی نگرانی کے ٹولز (Firebase Performance Monitoring) اس طرح کام کرتے ہیں۔
Method Swizzling ایک اعلی خطرے والی تکنیک ہے۔ لائبریریوں کے درمیان تنازعات: اگر دو لائبریریاں ایک ہی طریقہ کو swizzle کرتی ہیں، تو عملدرآمد کی ترتیب کی ضمانت نہیں ہے۔ iOS اپ ڈیٹس کے ساتھ عدم مطابقت: اگر Apple کسی نئے iOS ورژن میں دستخط تبدیل کرے یا طریقہ ہٹائے، تو swizzling کریش کا سبب بنتا ہے۔ کوڈ میں مرئیت کی کمی: swizzling کلاس کے نفاذ میں نظر نہیں آتا، جس سے ڈیبگنگ مشکل ہو جاتی ہے۔
| خطرہ | تفصیل | کم کرنا |
|---|---|---|
| لائبریری تنازع | دو لائبریریاں viewDidAppear کو swizzle کرتی ہیں — ایک دوسری کو توڑ دیتی ہے | class_getInstanceMethod کے ذریعے چیک کریں کہ طریقہ پہلے سے swizzled ہے یا نہیں |
| تکرار | اسی طریقہ کو دوبارہ swizzle کرنے سے لامحدود لوپ ہوتا ہے | ہمیشہ dispatch_once استعمال کریں |
| دستخط کی تبدیلی | Apple نئے iOS میں طریقہ کا دستخط تبدیل کرتا ہے — IMP مماثلت نہیں | تمام معاون iOS ورژنز پر ٹیسٹ کریں |
| غیر مرئی | Swizzling Xcode کال اسٹیک میں ظاہر نہیں ہوتا | کوڈ میں تمام swizzling کارروائیوں کو دستاویز کریں |
| App Review | Apple غیر دستاویزی swizzling والی ایپلیکیشنز کو مسترد کرتا ہے | صرف عوامی APIs استعمال کریں اور مقصد کو دستاویز کریں |
محفوظ swizzling کے لیے بہترین طرز عمل میں شامل ہیں: ہمیشہ اصل نفاذ کو کال کریں، dispatch_once کے ذریعے +load میں سختی سے swizzling کریں، swizzled طریقوں کو سابقہ (مثلاً s_originalMethodName) کے ساتھ نام دیں، ہر swizzling کارروائی کو اس کے مقصد کے ساتھ دستاویز کریں۔ Aspects لائبریری اصل طریقہ سے پہلے/بعد بلاکس کی زنجیری عملدرآمد کے ذریعے تنازع کے مسئلے کو حل کرتی ہے۔
Method swizzling کے متبادل پیش قیاسی اور حفاظت کی وجہ سے پروڈکشن کوڈ کے لیے ترجیحی ہیں۔ ڈیلیگیٹ اور پروٹوکول (UIApplicationDelegate, UITableViewDelegate) رن ٹائم کو تبدیل کیے بغیر واضح توسیعی نکات فراہم کرتے ہیں۔ ذیلی کلاس بنانا — viewDidAppear کو اوور رائڈ کرنے والا UIViewController کا ذیلی کلاس بنانا — پیش قیاسی طور پر کام کرتا ہے اور اس میں کوئی تنازع نہیں ہے۔
SwiftUI اور Combine swizzling کی ضرورت کو ختم کرتے ہیں: موڈیفائر (onAppear, onChange) طریقوں کو اوور رائڈ کیے بغیر اعلانیہ طور پر رویہ شامل کرتے ہیں۔ Android Jetpack Compose میں اثرات (LaunchedEffect, SideEffect) اور موڈیفائر کے ذریعے وہی حاصل کیا جاتا ہے۔ AOP فریم ورک (Android کے لیے AspectJ، iOS کے لیے InterposeKit) تعمیراتی وقت کی weaving کے ساتھ ایک محفوظ متبادل فراہم کرتے ہیں۔
Apple WWDC 2024 کے اعداد و شمار کے مطابق، Swift رن ٹائم زبان کی سطح پر method swizzling کی حمایت نہیں کرتا — @objc dynamic طریقوں کو صرف Objective-C Runtime کے ذریعے swizzle کیا جا سکتا ہے۔ جو Swift ایپلیکیشنز @objc استعمال نہیں کرتیں، وہ تیسرے فریق کی لائبریریوں کے ذریعے حادثاتی swizzling سے مکمل طور پر محفوظ ہیں۔ یہ Swift کو زیادہ محفوظ بناتا ہے لیکن رن ٹائم آلات سازی کی صلاحیتوں کو محدود کرتا ہے۔
SwiftUI موڈیفائر (onAppear, onChange, onReceive) اور Jetpack Compose اثرات (LaunchedEffect, SideEffect, DisposableEffect) UI کاموں کے لیے swizzling کو مکمل طور پر بدل دیتے ہیں۔ وہ ڈسپیچ ٹیبل کو تبدیل کیے بغیر کراس کٹنگ رویہ شامل کرنے کا ایک اعلانیہ، پیش قیاسی اور قابل جانچ طریقہ فراہم کرتے ہیں۔ نئے پروجیکٹس میں، Apple اور Google رن ٹائم مداخلت کے بجائے اس طریقہ کار کی سفارش کرتے ہیں۔
اکثر پوچھے گئے سوالات
Method Swizzling پروڈکشن کے لیے قابل قبول ہے جب اصولوں پر عمل کیا جائے: ایک بار عملدرآمد کے لیے dispatch_once، اصل نفاذ کو کال کرنا، تمام iOS ورژنز پر ٹیسٹنگ اور دستاویز کاری۔ سادہ کاموں کے لیے، ڈیلیگیٹ یا ذیلی کلاس بنانا بہتر ہے۔ پروڈکشن میں Swizzling مانیٹرنگ اور اینالیٹکس لائبریریوں کے لیے جائز ہے۔
Method Swizzling ڈسپیچ ٹیبل میں IMPs کو تبدیل کرنے کی ایک مخصوص تکنیک ہے۔ AOP (پہلو پر مبنی پروگرامنگ) ایک نمونہ ہے جس میں swizzling کو ایک میکانزم کے طور پر استعمال کیا جا سکتا ہے۔ AOP میں تعمیراتی وقت کی weaving (AspectJ)، پراکسی پر مبنی مداخلت (Spring AOP) اور کوڈ جنریشن بھی شامل ہے۔
تمام پیغامات کو ٹریک کرنے کے لیے objc_msgSend میں بریک پوائنٹ استعمال کریں۔ کلاس کے نام پر شرط کے ساتھ method_exchangeImplementations پر علامتی بریک پوائنٹ شامل کریں۔ FLEX ٹول دکھاتا ہے کہ کلاس کے کون سے طریقے swizzled ہیں۔ منظم جانچ کے لیے، lldb اسکرپٹ استعمال کریں جو کلاس کا ڈسپیچ ٹیبل آؤٹ پٹ کرے۔
Swift زبان کی سطح پر swizzling کی حمایت نہیں کرتا۔ Method Swizzling صرف @objc dynamic سے نشان زد طریقوں کے لیے کام کرتا ہے، جو Objective-C Runtime کے ذریعے مرتب ہوتے ہیں۔ خالص Swift طریقے (بغیر @objc کے) جامد تقسیم استعمال کرتے ہیں اور swizzle نہیں کیے جا سکتے — ان کا ڈسپیچ ٹیبل تبدیلی کے لیے قابل رسائی نہیں ہے۔
Firebase Analytics (خودکار اسکرین ٹریکنگ کے لیے viewDidAppear کا swizzling)، Amplitude، Mixpanel، FLEX (UI معائنہ)، OHHTTPStubs (نیٹ ورک درخواستوں کی نقل)، Aspects (AOP فریم ورک)۔ یہ سب اصل نفاذ کو کال کرتے ہوئے dispatch_once کے ذریعے +load میں swizzling کرتی ہیں۔
خلاصہ
ہم ایک موبائل ایپلیکیشن ٹرنکی تیار کریں گے
IT Sectr 2017 سے اسٹارٹ اپس اور کاروبار کے لیے iOS اور Android ایپلیکیشنز بناتا ہے۔ ہم آپ کو مشورہ دیں گے اور بہترین حل تجویز کریں گے۔
مزید پڑھیں