Method Swizzling trong phát triển iOS và Android: khái niệm chính, kỹ thuật và nguyên lý hoạt động

Tác giả: IT Sectr Đã đăng: 2026-05-17 Thời gian đọc: 9 phút

Method Swizzling là một kỹ thuật runtime trong đó việc triển khai của hai phương thức lớp được hoán đổi cho nhau trong khi thực thi. Nó cho phép ghi đè hoặc bổ sung hành vi của một phương thức hệ thống mà không cần tạo lớp con hay sửa đổi mã nguồn. Kỹ thuật này được ứng dụng nhiều nhất trong phát triển iOS với Objective-C, nhưng các phương thức tương tự cũng tồn tại trong Kotlin/Android thông qua reflection. Theo NSHipster Guide by Mattt, 2024, swizzling là một trong những cơ chế mạnh mẽ nhất, nhưng cũng nguy hiểm nhất của Objective-C Runtime.

Những điểm chính

  • Method Swizzling — hoán đổi triển khai của hai phương thức Objective-C trong runtime thông qua sel_registerName và method_exchangeImplementations.
  • Objective-C Runtime cho phép swizzling nhờ điều phối động thông qua objc_msgSend và bảng điều phối.
  • Swizzling trên Android được triển khai qua Java Reflection với việc thay thế implementation trong tệp dex hoặc qua Gradle Transform API.
  • Rủi ro của swizzling — xung đột giữa các thư viện, không tương thích với các bản cập nhật iOS, sập khi thay đổi chữ ký phương thức.
  • Swizzling an toàn yêu cầu dispatch_once, tính nguyên tử và gọi triển khai gốc bên trong phương thức đã swizzle.

Method Swizzling là gì?

Method Swizzling là một kỹ thuật runtime hoán đổi triển khai của hai phương thức Objective-C. Sau khi swizzling, gọi originalSelector sẽ thực thi mã của swizzledSelector và ngược lại. Điều này có được nhờ kiến trúc của Objective-C Runtime, nơi mỗi bộ chọn (SEL) được liên kết với một triển khai (IMP) thông qua bảng điều phối — một bảng có thể được sửa đổi trong khi thực thi.

Thuật ngữ “swizzling” được giới thiệu trong cộng đồng nhà phát triển Cocoa vào đầu những năm 2000. Kỹ thuật này trở nên nổi tiếng nhờ các thư viện như: AFNetworking (swizzling UIWebView để theo dõi tải), Aspects (framework AOP dựa trên swizzling) và FLEX (công cụ gỡ lỗi swizzle các phương thức hệ thống để kiểm tra). Ngày nay, swizzling được sử dụng ngầm trong hầu hết các ứng dụng iOS — thông qua các thư viện giám sát và phân tích.

Một thuộc tính quan trọng của swizzling là tính toàn cục: việc thay thế triển khai xảy ra ở cấp lớp, không phải cấp thể hiện. Nếu một thư viện swizzle phương thức UIViewController.viewDidLoad, nó ảnh hưởng đến TẤT CẢ các thể hiện UIViewController trong ứng dụng, bao gồm cả của hệ thống. Đây vừa là sức mạnh của swizzling — một dòng mã thay đổi hành vi của toàn bộ ứng dụng — vừa là nguồn gốc chính của lỗi.

Cách Method Swizzling hoạt động trong Objective-C

Objective-C Runtime lưu trữ trong mỗi lớp một bảng điều phối — một từ điển nơi khóa là SEL (định danh phương thức) và giá trị là IMP (con trỏ đến hàm triển khai). Khi một ứng dụng gửi tin nhắn đến một đối tượng, objc_msgSend thực hiện tìm kiếm tuyến tính trong bảng này. Method Swizzling thay thế IMP của một SEL bằng IMP của một SEL khác, chuyển hướng các lời gọi.

objective-c
// Triển khai method swizzling an toàn
@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

Hàm chính là method_exchangeImplementations(Method, Method). Nó hoán đổi nguyên tử các IMP của hai đối tượng Method. Sau lời gọi, bảng điều phối của lớp bị sửa đổi: gọi original thực thi mã swizzled, gọi swizzled thực thi mã original. Danh mục SafeSwizzle thêm phương thức này vào tất cả NSObject, cho phép bất kỳ lớp nào thực hiện swizzling.

Một triển khai swizzling an toàn yêu cầu gọi triển khai gốc bên trong phiên bản đã swizzle. Nếu không, hành vi gốc của phương thức sẽ bị mất vĩnh viễn. Mẫu đúng là lưu IMP gốc trước khi hoán đổi và gọi nó trong phương thức đã swizzle:

objective-c
// Swizzling với gọi triển khai gốc
- (void)swizzled_viewDidLoad {
    // 1. Gọi triển khai gốc
    [self swizzled_viewDidLoad];

    // 2. Logic bổ sung sau lời gọi gốc
    NSLog("viewDidLoad đã thực thi, swizzling đang hoạt động");
}

+ (void)load {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        [self swizzleClassMethod:@selector(viewDidLoad)
                            with:@selector(swizzled_viewDidLoad)];
    });
}

dispatch_once đảm bảo rằng swizzling thực thi đúng một lần trong suốt vòng đời của ứng dụng. Swizzling lại cùng một phương thức sẽ dẫn đến đệ quy vô hạn: phương thức đã swizzle sẽ gọi chính nó. +load được gọi khi lớp được tải vào runtime — đây là điểm an toàn cho swizzling, thực thi trước mã chính của ứng dụng.

Cấu trúc của bảng điều phối

Bảng điều phối của một lớp Objective-C là một mảng các cấu trúc method_t chứa SEL, IMP và kiểu trả về. method_exchangeImplementations chỉ đơn giản hoán đổi hai con trỏ IMP trong bảng này. Quan trọng: swizzling chỉ hoạt động ở cấp lớp, không phải cấp giao thức. Nếu một phương thức được định nghĩa trong giao thức nhưng không được triển khai, bảng điều phối không chứa mục nhập cho swizzling.

Tác động của swizzling đến hiệu suất

Chi phí của swizzling là tối thiểu — việc hoán đổi hai con trỏ IMP trong bảng điều phối mất vài nano giây. Sau khi swizzling, việc điều phối phương thức không bị chậm lại: objc_msgSend tìm IMP trong cùng thời gian O(1) như trước khi swizzling. Thao tác bổ sung duy nhất là kiểm tra bộ nhớ đệm phương thức ở lần gọi đầu tiên sau khi hoán đổi. Theo dữ liệu của Apple Performance Team, swizzling không ảnh hưởng đến hiệu suất ứng dụng.

Ứng dụng của Method Swizzling trong iOS

Method Swizzling được sử dụng trong ba kịch bản chính: giám sát và phân tích (theo dõi viewDidLoad, viewDidAppear để tự động gửi sự kiện), chặn AOP (ghi nhật ký tham số của tất cả các lời gọi phương thức) và hotfix (sửa lỗi sản xuất mà không cần App Store Review thông qua các thư viện như JSPatch).

  • Phân tích tự động — swizzling UIViewController.viewDidAppear để gửi sự kiện screen view mà không trùng lặp mã trong mọi bộ điều khiển.
  • Ghi nhật ký yêu cầu mạng — swizzling NSURLSession.resume để theo dõi tất cả các yêu cầu HTTP, bao gồm cả từ các thư viện bên thứ ba.
  • AOP (Lập trình hướng khía cạnh) — thư viện Aspects swizzle các phương thức và thực thi một khối mã trước/sau/thay cho lời gọi gốc.
  • Hotfix — thay thế triển khai của một phương thức bị lỗi bằng phiên bản đã sửa mà không cần xây dựng lại ứng dụng (bị App Review cấm từ năm 2020).
  • Kiểm thử và mock — OCMock sử dụng swizzling để thay thế các phương thức bằng triển khai mock trong kiểm thử đơn vị.

Mỗi kịch bản này hoạt động vì swizzling được áp dụng tập trung. Một thư viện phân tích thực hiện swizzling một lần trong +load, và tất cả các thể hiện UIViewController trong ứng dụng bắt đầu gửi sự kiện. Nhà phát triển không cần thêm mã vào mọi bộ điều khiển — điều này giảm sự trùng lặp và rủi ro lỗi.

Method Swizzling trên Android: reflection và thao tác bytecode

Trên Android, method swizzling theo nghĩa cổ điển của Objective-C là không thể — Java/Kotlin sử dụng điều phối tĩnh qua vtable. Tuy nhiên, tồn tại các cơ chế đạt được hiệu ứng tương tự: Java Reflection để thay thế triển khai trong runtime và Gradle Transform API / ASM để sửa đổi bytecode tại thời điểm xây dựng.

kotlin
// Swizzling trên Android qua reflection + companion object
class Logger {
    companion object {
        var originalImpl: (() -> Unit)? = null
    }

    fun log() {
        println("nhật ký gốc")
    }
}

// Thay thế triển khai trong runtime qua reflection
fun swizzleLog() {
    val originalMethod = Logger::class.java
        .getDeclaredMethod("log")
    originalMethod.isAccessible = true

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

    // Thay thế qua hàm inline
    println("swizzled: nhật ký đã bị chặn")
}

Mã này thay thế hành vi của phương thức log() thông qua Java Reflection: getDeclaredMethod truy cập triển khai riêng tư, isAccessible vô hiệu hóa kiểm tra truy cập. Thay vì gọi trực tiếp log(), một trình bao bọc được gọi để thực thi logic bổ sung. Tuy nhiên, Android tối ưu hóa các phương thức nóng qua JIT — reflection có thể không hoạt động trên các phân đoạn AOT đã được biên dịch.

Một cách tiếp cận đáng tin cậy hơn là thao tác bytecode thông qua Gradle Transform API hoặc AGP (Android Gradle Plugin) với thư viện ASM. Việc sửa đổi bytecode được thực hiện tại thời điểm biên dịch: ASM thêm các lời gọi vào mọi phương thức của lớp. Đây là cách các công cụ đo lường phủ mã (JaCoCo) và giám sát hiệu suất (Firebase Performance Monitoring) hoạt động.

Rủi ro và thực hành tốt nhất của Method Swizzling

Method Swizzling là một kỹ thuật có rủi ro cao. Xung đột giữa các thư viện: nếu hai thư viện swizzle cùng một phương thức, thứ tự thực thi không được đảm bảo. Không tương thích với các bản cập nhật iOS: nếu Apple thay đổi chữ ký hoặc xóa một phương thức trong phiên bản iOS mới, swizzling dẫn đến sập. Thiếu khả năng hiển thị trong mã: swizzling không hiển thị trong triển khai lớp, gây khó khăn cho việc gỡ lỗi.

Rủi roMô tảGiảm thiểu
Xung đột thư việnHai thư viện swizzle viewDidAppear — một cái phá vỡ cái kiaKiểm tra xem phương thức đã được swizzle chưa qua class_getInstanceMethod
Đệ quySwizzling lại cùng phương thức gây ra vòng lặp vô hạnLuôn sử dụng dispatch_once
Thay đổi chữ kýApple thay đổi chữ ký phương thức trong iOS mới — IMP không khớpKiểm thử trên tất cả các phiên bản iOS được hỗ trợ
Không hiển thịSwizzling không xuất hiện trong ngăn xếp cuộc gọi XcodeGhi lại tất cả các thao tác swizzling trong mã
App ReviewApple từ chối ứng dụng có swizzling không được ghi lạiChỉ sử dụng API công khai và ghi lại mục đích

Các thực hành tốt nhất để swizzling an toàn bao gồm: luôn gọi triển khai gốc, thực hiện swizzling nghiêm ngặt trong +load qua dispatch_once, đặt tên các phương thức đã swizzle với tiền tố (ví dụ: s_originalMethodName), ghi lại từng thao tác swizzling với mục đích của nó. Thư viện Aspects giải quyết vấn đề xung đột thông qua thực thi chuỗi các khối trước/sau phương thức gốc.

Các lựa chọn thay thế Method Swizzling trong phát triển hiện đại

Các lựa chọn thay thế method swizzling được ưu tiên cho mã sản xuất vì tính dự đoán được và an toàn. Các delegate và giao thức (UIApplicationDelegate, UITableViewDelegate) cung cấp các điểm mở rộng rõ ràng mà không sửa đổi runtime. Tạo lớp con — tạo một lớp con của UIViewController ghi đè viewDidAppear — hoạt động dự đoán được và không có xung đột.

SwiftUI và Combine loại bỏ nhu cầu về swizzling: các bổ từ (onAppear, onChange) thêm hành vi một cách khai báo, mà không ghi đè phương thức. Trong Android Jetpack Compose đạt được điều tương tự thông qua các hiệu ứng (LaunchedEffect, SideEffect) và bổ từ. Các framework AOP (AspectJ cho Android, InterposeKit cho iOS) cung cấp một lựa chọn thay thế an toàn với tính năng dệt tại thời điểm biên dịch.

Theo dữ liệu từ Apple WWDC 2024, runtime Swift không hỗ trợ method swizzling ở cấp ngôn ngữ — các phương thức @objc dynamic chỉ có thể được swizzle thông qua Objective-C Runtime. Các ứng dụng Swift không sử dụng @objc được bảo vệ hoàn toàn khỏi swizzling ngẫu nhiên bởi các thư viện bên thứ ba. Điều này làm cho Swift an toàn hơn nhưng hạn chế khả năng đo lường runtime.

Các lựa chọn khai báo trong SwiftUI và Compose

Các bổ từ SwiftUI (onAppear, onChange, onReceive) và các hiệu ứng Jetpack Compose (LaunchedEffect, SideEffect, DisposableEffect) thay thế hoàn toàn swizzling cho các tác vụ UI. Chúng cung cấp một cách khai báo, dự đoán được và có thể kiểm thử để thêm hành vi xuyên suốt mà không sửa đổi bảng điều phối. Trong các dự án mới, Apple và Google khuyến nghị cách tiếp cận này thay vì chặn runtime.

Các câu hỏi thường gặp

Method Swizzling có an toàn cho sản xuất không?

Method Swizzling có thể chấp nhận được cho sản xuất nếu tuân thủ các quy tắc: dispatch_once để thực thi một lần, gọi triển khai gốc, kiểm thử trên tất cả các phiên bản iOS và ghi lại tài liệu. Đối với các tác vụ đơn giản, tốt hơn nên sử dụng delegate hoặc tạo lớp con. Swizzling trong sản xuất được biện minh cho các thư viện giám sát và phân tích.

Swizzling khác với AOP như thế nào?

Method Swizzling là một kỹ thuật cụ thể để thay thế IMP trong bảng điều phối. AOP (Lập trình hướng khía cạnh) là một mô hình trong đó swizzling có thể được sử dụng như một trong các cơ chế. AOP cũng bao gồm dệt tại thời điểm biên dịch (AspectJ), chặn dựa trên proxy (Spring AOP) và tạo mã.

Làm thế nào để gỡ lỗi các vấn đề do Swizzling gây ra?

Sử dụng điểm dừng trong objc_msgSend để theo dõi tất cả các tin nhắn. Thêm điểm dừng tượng trưng trên method_exchangeImplementations với điều kiện về tên lớp. Công cụ FLEX hiển thị những phương thức nào của lớp đã bị swizzle. Để kiểm tra có hệ thống, sử dụng tập lệnh lldb xuất bảng điều phối của lớp.

Swizzling có hoạt động trong Swift không?

Swift không hỗ trợ swizzling ở cấp ngôn ngữ. Method Swizzling chỉ hoạt động cho các phương thức được đánh dấu @objc dynamic, được biên dịch thông qua Objective-C Runtime. Các phương thức Swift thuần túy (không có @objc) sử dụng điều phối tĩnh và không thể bị swizzle — bảng điều phối của chúng không thể truy cập để sửa đổi.

Những thư viện iOS nào sử dụng Swizzling?

Firebase Analytics (swizzling viewDidAppear để theo dõi màn hình tự động), Amplitude, Mixpanel, FLEX (kiểm tra UI), OHHTTPStubs (mô phỏng yêu cầu mạng), Aspects (framework AOP). Tất cả đều thực hiện swizzling trong +load qua dispatch_once với lời gọi đến triển khai gốc.

Tổng kết

  • Method Swizzling — hoán đổi IMP của hai phương thức trong bảng điều phối Objective-C Runtime thông qua method_exchangeImplementations.
  • dispatch_once là bắt buộc để ngăn swizzling lại và đệ quy.
  • Gọi triển khai gốc bên trong phương thức đã swizzle là quy tắc an toàn bắt buộc.
  • Trên Android, swizzling được thay thế bằng reflection hoặc thao tác bytecode qua Gradle Transform / ASM.
  • Rủi ro — xung đột thư viện, không tương thích với các phiên bản iOS, không hiển thị trong trình gỡ lỗi và lệnh cấm App Review cho hotfix.
  • Lựa chọn thay thế — delegate, tạo lớp con, bổ từ SwiftUI, hiệu ứng Jetpack Compose.
  • Các phương thức Swift không có @objc dynamic được bảo vệ khỏi swizzling, cải thiện độ ổn định nhưng hạn chế khả năng đo lường runtime.

Chúng tôi sẽ phát triển ứng dụng di động chìa khóa trao tay

IT Sectr tạo các ứng dụng iOS và Android cho các công ty khởi nghiệp và doanh nghiệp từ năm 2017. Chúng tôi sẽ tư vấn và đề xuất giải pháp tốt nhất cho bạn.

Thảo luận dự án

Đọc thêm