Method Swizzling در توسعه iOS و Android: مفاهیم کلیدی، تکنیک‌ها و اصل کار

نویسنده: IT Sectr منتشر شده: 2026-05-17 زمان مطالعه: 9 دقیقه

Method Swizzling — تکنیک runtime است که در آن پیاده‌سازی دو متد از یک کلاس در زمان اجرا جابه‌جا می‌شوند. این امکان را فراهم می‌کند تا بدون ایجاد زیرکلاس و بدون تغییر کد منبع، رفتار یک متد سیستمی را بازنویسی یا گسترش دهید. این تکنیک بیشترین کاربرد را در توسعه iOS با Objective-C دارد، اما مشابه‌هایی در Kotlin/Android از طریق reflection وجود دارد. به گفته NSHipster Guide by Mattt, 2024، swizzling یکی از قدرتمندترین، اما همچنین خطرناک‌ترین مکانیسم‌های Objective-C Runtime است.

نکات اصلی

  • Method Swizzling — تبادل پیاده‌سازی دو متد Objective-C در runtime از طریق sel_registerName و method_exchangeImplementations.
  • Objective-C Runtime به لطف توزیع پویا از طریق objc_msgSend و dispatch table، swizzling را فراهم می‌کند.
  • Swizzling در Android از طریق Java Reflection با جایگزینی پیاده‌سازی در فایل‌های dex یا از طریق Gradle Transform API پیاده‌سازی می‌شود.
  • خطرات swizzling — تداخل بین کتابخانه‌ها، ناسازگاری با به‌روزرسانی‌های iOS، کرش در هنگام تغییر امضای متدها.
  • Swizzling ایمن نیاز به dispatch_once، اتمیک بودن و فراخوانی پیاده‌سازی اصلی در داخل متد swizzled دارد.

Method Swizzling چیست؟

Method Swizzling — تکنیک runtime است که پیاده‌سازی دو متد Objective-C را در زمان اجرا جابجا می‌کند. پس از swizzling، فراخوانی originalSelector منجر به اجرای کد swizzledSelector می‌شود و بالعکس. این به لطف معماری Objective-C Runtime امکان‌پذیر است، جایی که هر selector (SEL) از طریق dispatch table — جدولی که می‌توان در زمان اجرا تغییر داد — به پیاده‌سازی (IMP) متصل است.

اصطلاح «swizzling» در اوایل دهه 2000 در جامعه توسعه‌دهندگان Cocoa معرفی شد. این تکنیک به لطف کتابخانه‌های زیر شهرت گسترده‌ای یافت: AFNetworking (swizzling UIWebView برای ردیابی بارگذاری)، Aspects (چارچوب AOP مبتنی بر swizzling) و FLEX (ابزار اشکال‌زدایی که متدهای سیستمی را برای بازرسی swizzle می‌کند). امروزه swizzling در اکثر برنامه‌های iOS به صورت ضمنی — از طریق کتابخانه‌های مانیتورینگ و آنالیتیکس — استفاده می‌شود.

ویژگی مهم swizzling — جهانی بودن: جایگزینی پیاده‌سازی در سطح کلاس اتفاق می‌افتد، نه نمونه. اگر کتابخانه‌ای متد UIViewController.viewDidLoad را swizzle کند، این روی تمام نمونه‌های UIViewController در برنامه تأثیر می‌گذارد، از جمله نمونه‌های سیستمی. این هم قدرت swizzling است — یک خط کد رفتار کل برنامه را تغییر می‌دهد — و هم منبع اصلی باگ‌ها.

Method Swizzling در Objective-C چگونه کار می‌کند

Objective-C Runtime در هر کلاس یک dispatch table — فرهنگ لغتی که کلید آن SEL (شناسه متد) و مقدار آن IMP (اشاره‌گر به تابع پیاده‌سازی) است — ذخیره می‌کند. وقتی برنامه پیامی به یک شیء می‌فرستد، objc_msgSend جستجوی خطی در این جدول انجام می‌دهد. Method Swizzling IMP یک SEL را با IMP یک SEL دیگر جایگزین می‌کند و فراخوانی‌ها را هدایت می‌کند.

objective-c
// پیاده‌سازی 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). این تابع به صورت اتمی IMP دو شیء Method را جابجا می‌کند. پس از فراخوانی، dispatch table کلاس تغییر می‌کند: هنگام دسترسی به original، کد swizzled اجرا می‌شود و هنگام دسترسی به swizzled، کد original. دسته SafeSwizzle این متد را به همه NSObject اضافه می‌کند و به هر کلاسی امکان اجرای swizzling را می‌دهد.

پیاده‌سازی ایمن swizzling نیاز به فراخوانی original implementation در داخل نسخه swizzled دارد. در غیر این صورت، رفتار اصلی متد به طور غیرقابل برگشتی از دست می‌رود. الگوی صحیح — ذخیره IMP اصلی قبل از تبادل و فراخوانی آن در متد swizzled:

objective-c
// 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 دقیقاً یک بار در طول عمر برنامه اجرا شود. swizzling مجدد همان متد منجر به بازگشت بی‌نهایت می‌شود: متد swizzled خودش را فراخوانی می‌کند. +load هنگام بارگذاری کلاس در runtime فراخوانی می‌شود — این نقطه امنی برای swizzling است که قبل از کد اصلی برنامه اجرا می‌شود.

آناتومی dispatch table

Dispatch table کلاس Objective-C — آرایه‌ای از ساختارهای method_t است که شامل SEL، IMP و نوع مقدار بازگشتی است. method_exchangeImplementations به سادگی دو اشاره‌گر IMP را در این جدول جابجا می‌کند. مهم: swizzling فقط در سطح کلاس کار می‌کند، نه پروتکل. اگر متدی در پروتکل تعریف شده باشد اما پیاده‌سازی نشده باشد — dispatch table حاوی ورودی برای swizzling نیست.

تأثیر swizzling بر عملکرد

سربار swizzling حداقل است — جابجایی دو اشاره‌گر IMP در dispatch table چند نانوثانیه طول می‌کشد. پس از swizzling، فراخوانی متد کند نمی‌شود: objc_msgSend IMP را در همان زمان O(1) قبل از swizzling پیدا می‌کند. تنها عملیات اضافی — بررسی method cache در اولین فراخوانی پس از تبادل. به گفته Apple Performance Team، swizzling بر عملکرد برنامه تأثیر نمی‌گذارد.

کاربرد Method Swizzling در iOS

Method Swizzling در سه سناریوی اصلی استفاده می‌شود: مانیتورینگ و آنالیتیکس (ردیابی viewDidLoad، viewDidAppear برای ارسال خودکار رویدادها)، رهگیری AOP (ثبت پارامترهای تمام فراخوانی‌های متد) و hotfix (رفع باگ در production بدون App Store Review از طریق کتابخانه‌هایی مانند JSPatch).

  • آنالیتیکس خودکار — swizzling UIViewController.viewDidAppear برای ارسال رویداد screen view بدون تکرار کد در هر کنترلر.
  • ثبت درخواست‌های شبکه — swizzling NSURLSession.resume برای ردیابی تمام درخواست‌های HTTP، از جمله کتابخانه‌های شخص ثالث.
  • AOP (برنامه‌نویسی جنبه‌گرا) — کتابخانه Aspects متدها را swizzle می‌کند و بلوک کد را قبل/بعد/به جای فراخوانی اصلی اجرا می‌کند.
  • Hotfix — جایگزینی پیاده‌سازی متد معیوب با نسخه اصلاح‌شده بدون بازسازی برنامه (از 2020 توسط App Review ممنوع شده است).
  • تست و mock — OCMock از swizzling برای جایگزینی متدها با پیاده‌سازی‌های mock در تست‌های واحد استفاده می‌کند.

هر یک از این سناریوها به لطف این کار می‌کند که swizzling به صورت متمرکز اعمال می‌شود. کتابخانه آنالیتیکس یک بار در +load swizzling را انجام می‌دهد و تمام UIViewController در برنامه شروع به ارسال رویدادها می‌کنند. توسعه‌دهنده نیازی به افزودن کد در هر کنترلر ندارد — این کار تکرار و خطر خطاها را کاهش می‌دهد.

Method Swizzling در Android: reflection و bytecode manipulation

در Android method swizzling به معنای کلاسیک Objective-C ممکن نیست — Java/Kotlin از توزیع ایستا از طریق vtable استفاده می‌کنند. با این حال، مکانیسم‌هایی وجود دارند که به اثر مشابهی دست می‌یابند: Java Reflection برای جایگزینی پیاده‌سازی در runtime و Gradle Transform API / ASM برای تغییر بایت‌کد در مرحله ساخت.

kotlin
// Swizzling در Android از طریق reflection + companion object
class Logger {
    companion object {
        var originalImpl: (() -> Unit)? = null
    }

    fun log() {
        println("log اصلی")
    }
}

// جایگزینی پیاده‌سازی در runtime از طریق reflection
fun swizzleLog() {
    val originalMethod = Logger::class.java
        .getDeclaredMethod("log")
    originalMethod.isAccessible = true

    Logger.originalImpl = {
        originalMethod.invoke(Logger())
    }

    // جایگزینی از طریق تابع inline
    println("swizzled: log رهگیری شد")
}

این کد رفتار متد log() را از طریق Java Reflection تغییر می‌دهد: getDeclaredMethod به پیاده‌سازی خصوصی دسترسی پیدا می‌کند، isAccessible بررسی دسترسی را غیرفعال می‌کند. به جای فراخوانی مستقیم log()، یک لفاف که منطق اضافی را اجرا می‌کند فراخوانی می‌شود. با این حال، Android متدهای داغ را از طریق JIT بهینه‌سازی می‌کند — reflection ممکن است در بخش‌های قبلاً کامپایل‌شده AOT کار نکند.

رویکرد قابل اعتمادتر — bytecode manipulation از طریق Gradle Transform API یا AGP (Android Gradle Plugin) با کتابخانه ASM. تغییر بایت‌کد در مرحله کامپایل انجام می‌شود: ASM فراخوانی‌هایی را به هر متد کلاس اضافه می‌کند. ابزارهای پوشش کد (JaCoCo) و مانیتورینگ عملکرد (Firebase Performance Monitoring) اینگونه کار می‌کنند.

خطرات و best practices Method Swizzling

Method Swizzling — تکنیکی با ریسک بالا. تداخل بین کتابخانه‌ها: اگر دو کتابخانه یک متد را swizzle کنند، ترتیب اجرا تضمین نمی‌شود. ناسازگاری با به‌روزرسانی‌های iOS: اگر Apple امضا را تغییر دهد یا متدی را در نسخه جدید iOS حذف کند، swizzling منجر به کرش می‌شود. عدم دید در کد: swizzling در پیاده‌سازی کلاس قابل مشاهده نیست که اشکال‌زدایی را دشوار می‌کند.

ریسکتوضیحاتکاهش
تداخل کتابخانه‌هادو کتابخانه viewDidAppear را swizzle می‌کنند — یکی دیگری را خراب می‌کندبررسی کنید که آیا متد قبلاً از طریق class_getInstanceMethod swizzle شده است
بازگشتswizzling مجدد همان متد باعث حلقه بی‌نهایت می‌شودهمیشه از dispatch_once استفاده کنید
تغییر امضاApple امضای متد را در iOS جدید تغییر می‌دهد — IMP مطابقت ندارددر تمام نسخه‌های پشتیبانی‌شده iOS تست کنید
عدم دیدSwizzling در پشته فراخوانی Xcode نشان داده نمی‌شودتمام عملیات swizzling را در کد مستند کنید
App ReviewApple برنامه‌های با swizzling مستندنشده را رد می‌کندفقط از APIهای عمومی استفاده کنید و هدف را مستند کنید

Best practices برای swizzling ایمن شامل: همیشه پیاده‌سازی اصلی را فراخوانی کنید، swizzling را دقیقاً در +load از طریق dispatch_once اجرا کنید، متدهای swizzled را با پیشوند نام‌گذاری کنید (مثلاً s_originalMethodName)، هر عملیات swizzling را با ذکر هدف مستند کنید. کتابخانه Aspects مشکل تداخل را از طریق اجرای زنجیره‌ای بلوک‌ها قبل/بعد از متد اصلی حل می‌کند.

جایگزین‌های Method Swizzling در توسعه مدرن

جایگزین‌های method swizzling به دلیل پیش‌بینی‌پذیری و امنیت برای کد production ترجیح داده می‌شوند. Delegateها و پروتکل‌ها (UIApplicationDelegate, UITableViewDelegate) نقاط گسترش صریح بدون تغییر runtime فراهم می‌کنند. زیرکلاس‌سازی — ایجاد زیرکلاس UIViewController با بازنویسی viewDidAppear — به صورت قابل پیش‌بینی کار می‌کند و تداخل ندارد.

SwiftUI و Combine نیاز به swizzling را از بین می‌برند: modifierها (onAppear, onChange) رفتار را به صورت اعلامی و بدون بازنویسی متدها اضافه می‌کنند. در Android، Jetpack Compose از طریق effectها (LaunchedEffect, SideEffect) و modifierها به همان نتیجه می‌رسد. چارچوب‌های AOP (AspectJ برای Android، InterposeKit برای iOS) جایگزین ایمن با compile-time weaving ارائه می‌دهند.

به گفته Apple WWDC 2024، Swift runtime از method swizzling در سطح زبان پشتیبانی نمی‌کند — متدهای @objc dynamic فقط از طریق Objective-C Runtime می‌توانند swizzle شوند. برنامه‌های Swift که از @objc استفاده نمی‌کنند، به طور کامل از swizzling تصادفی توسط کتابخانه‌های شخص ثالث محافظت می‌شوند. این Swift را امن‌تر می‌کند، اما قابلیت‌های ابزارسازی runtime را محدود می‌کند.

جایگزین‌های اعلامی در SwiftUI و Compose

SwiftUI modifierها (onAppear, onChange, onReceive) و Jetpack Compose effectها (LaunchedEffect, SideEffect, DisposableEffect) به طور کامل جایگزین swizzling برای وظایف UI می‌شوند. آنها روشی اعلامی، قابل پیش‌بینی و قابل تست برای افزودن رفتار مقطعی بدون تغییر dispatch table ارائه می‌دهند. در پروژه‌های جدید، Apple و Google دقیقاً این رویکرد را به جای رهگیری runtime توصیه می‌کنند.

سوالات متداول

آیا Method Swizzling برای production ایمن است؟

Method Swizzling در صورت رعایت قوانین برای production قابل قبول است: dispatch_once برای اجرای یک‌باره، فراخوانی پیاده‌سازی اصلی، تست در تمام نسخه‌های iOS و مستندسازی. برای کارهای ساده بهتر است از delegateها یا زیرکلاس‌سازی استفاده کنید. Swizzling در production برای کتابخانه‌های مانیتورینگ و آنالیتیکس موجه است.

Swizzling چه تفاوتی با AOP دارد؟

Method Swizzling — تکنیک خاص جایگزینی IMP در dispatch table است. AOP (برنامه‌نویسی جنبه‌گرا) — پارادایمی است که در آن swizzling می‌تواند به عنوان یکی از مکانیسم‌ها استفاده شود. AOP همچنین شامل compile-time weaving (AspectJ)، رهگیری مبتنی بر proxy (Spring AOP) و code generation است.

چگونه مشکلات ناشی از Swizzling را اشکال‌زدایی کنیم؟

از breakpoint در objc_msgSend برای ردیابی تمام پیام‌ها استفاده کنید. یک breakpoint نمادین روی method_exchangeImplementations با شرط روی نام کلاس اضافه کنید. ابزار FLEX نشان می‌دهد که کدام متدهای کلاس swizzle شده‌اند. برای بررسی سیستماتیک از اسکریپت lldb استفاده کنید که dispatch table کلاس را نمایش می‌دهد.

آیا Swizzling در Swift کار می‌کند؟

Swift از swizzling در سطح زبان پشتیبانی نمی‌کند. Method Swizzling فقط برای متدهایی که با @objc dynamic علامت‌گذاری شده‌اند و از طریق Objective-C Runtime کامپایل می‌شوند کار می‌کند. متدهای خالص Swift (بدون @objc) از توزیع ایستا استفاده می‌کنند و نمی‌توانند swizzle شوند — dispatch table آنها برای تغییر در دسترس نیست.

کدام کتابخانه‌های iOS از Swizzling استفاده می‌کنند؟

Firebase Analytics (swizzling viewDidAppear برای ردیابی خودکار صفحه)، Amplitude، Mixpanel، FLEX (بازرسی UI)، OHHTTPStubs (mock کردن درخواست‌های شبکه)، Aspects (چارچوب AOP). همه آنها swizzling را در +load از طریق dispatch_once با فراخوانی پیاده‌سازی اصلی انجام می‌دهند.

خلاصه

  • Method Swizzling — تبادل IMP دو متد در dispatch table Objective-C Runtime از طریق method_exchangeImplementations.
  • dispatch_once برای جلوگیری از swizzling مجدد و بازگشت الزامی است.
  • فراخوانی پیاده‌سازی اصلی در داخل متد swizzled — قانون ایمنی اجباری.
  • در Android swizzling با reflection یا bytecode manipulation از طریق Gradle Transform / ASM جایگزین می‌شود.
  • خطرات — تداخل کتابخانه‌ها، ناسازگاری با نسخه‌های iOS، عدم دید در دیباگر و ممنوعیت App Review برای hotfix.
  • جایگزین‌ها — delegateها، زیرکلاس‌سازی، modifierهای SwiftUI، effectهای Jetpack Compose.
  • متدهای Swift بدون @objc dynamic از swizzling محافظت می‌شوند که پایداری را افزایش می‌دهد اما ابزارسازی runtime را محدود می‌کند.

ما یک اپلیکیشن موبایل به صورت کلید در دست توسعه خواهیم داد

IT Sectr از سال 2017 برنامه‌های iOS و Android را برای استارتاپ‌ها و کسب‌وکارها ایجاد می‌کند. ما به شما مشاوره می‌دهیم و بهترین راه‌حل را پیشنهاد خواهیم کرد.

بحث درباره پروژه

همچنین بخوانید