iOS・Android開発におけるMethod Swizzling:主要な概念、テクニック、動作原理

著者: IT Sectr 公開日: 2026-05-17 読了時間: 9 分

Method Swizzlingは、実行時に2つのクラスメソッドの実装を入れ替えるランタイムテクニックです。サブクラスを作成したりソースコードを変更したりせずに、システムメソッドの動作をオーバーライドまたは補完できます。このテクニックはObjective-Cを使用したiOS開発で最も広く応用されていますが、Kotlin/Androidでもリフレクションを通じて類似の仕組みが存在します。NSHipster Guide by Mattt, 2024によると、swizzlingはObjective-C Runtimeの最も強力でありながら最も危険なメカニズムの1つです。

重要なポイント

  • Method Swizzling — sel_registerNameとmethod_exchangeImplementationsを使用して、実行時に2つのObjective-Cメソッドの実装を入れ替える。
  • Objective-C Runtimeは、objc_msgSendとディスパッチテーブルによる動的ディスパッチを通じてswizzlingを可能にする。
  • AndroidでのSwizzlingは、Java Reflectionを使用したdexファイル内の実装置換、またはGradle Transform APIを通じて実装される。
  • Swizzlingのリスク — ライブラリ間の競合、iOSアップデートとの非互換性、メソッドシグネチャ変更時のクラッシュ。
  • 安全なswizzlingには、dispatch_once、アトミック性、swizzledメソッド内での元の実装の呼び出しが必要。

Method Swizzlingとは?

Method Swizzlingは、2つのObjective-Cメソッドの実装を入れ替えるランタイムテクニックです。Swizzling後、originalSelectorを呼び出すとswizzledSelectorのコードが実行され、その逆も同様です。これは、各セレクタ(SEL)がディスパッチテーブルを介して実装(IMP)に関連付けられているObjective-C Runtimeのアーキテクチャのおかげで可能です。このテーブルは実行時に変更できます。

“swizzling”という用語は、2000年代初頭にCocoa開発者コミュニティで導入されました。このテクニックは、AFNetworking(読み込み追跡のためのUIWebViewのswizzling)、Aspects(swizzlingベースのAOPフレームワーク)、FLEX(システムメソッドをswizzleして検査するデバッグツール)などのライブラリによって広く認知されるようになりました。今日では、swizzlingはほとんどのiOSアプリケーションで暗黙的に使用されています。監視および分析ライブラリを通じてです。

Swizzlingの重要な特性はグローバル性です。実装の置換はインスタンスレベルではなくクラスレベルで発生します。ライブラリがUIViewController.viewDidLoadメソッドをswizzleすると、アプリケーション内のすべてのUIViewControllerインスタンス(システムのものも含む)に影響します。これはswizzlingの強み(1行のコードでアプリケーション全体の動作を変えられる)であると同時に、バグの主な原因でもあります。

Objective-CでのMethod Swizzlingの仕組み

Objective-C Runtimeは、各クラスにディスパッチテーブルを格納します。これは、キーがSEL(メソッド識別子)、値がIMP(実装関数へのポインタ)の辞書です。アプリケーションがオブジェクトにメッセージを送信すると、objc_msgSendがこのテーブルを線形検索します。Method Swizzlingは、あるSELのIMPを別のSELのIMPで置き換え、呼び出しをリダイレクトします。

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)です。これは2つのMethodオブジェクトのIMPをアトミックに交換します。呼び出し後、クラスのディスパッチテーブルが変更されます。originalを呼び出すとswizzledコードが実行され、swizzledを呼び出すとoriginalコードが実行されます。SafeSwizzleカテゴリはこのメソッドをすべてのNSObjectに追加し、任意のクラスがswizzlingを実行できるようにします。

安全なswizzlingの実装には、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がアプリケーションのライフタイム中に正確に1回だけ実行されることを保証します。同じメソッドを再度swizzlingすると無限再帰が発生します。swizzledメソッドが自分自身を呼び出すことになります。+loadはクラスがランタイムにロードされるときに呼び出されます。これはメインのアプリケーションコードより前に実行されるswizzlingの安全なポイントです。

ディスパッチテーブルの構造

Objective-Cクラスのディスパッチテーブルは、SEL、IMP、戻り値の型を含むmethod_t構造体の配列です。method_exchangeImplementationsは単にこのテーブル内の2つのIMPポインタを交換します。重要なのは、swizzlingはクラスレベルでのみ機能し、プロトコルレベルでは機能しないことです。メソッドがプロトコルで定義されていても実装されていない場合、ディスパッチテーブルにはswizzlingのエントリがありません。

パフォーマンスへの影響

Swizzlingのオーバーヘッドは最小限です。ディスパッチテーブル内の2つのIMPポインタの交換には数ナノ秒かかります。Swizzling後もメソッドディスパッチは遅くなりません。objc_msgSendはswizzling前と同じO(1)時間でIMPを見つけます。唯一の追加操作は、交換後の最初の呼び出しでのメソッドキャッシュのチェックです。Apple Performance Teamのデータによると、swizzlingはアプリケーションのパフォーマンスに影響しません。

iOSでのMethod Swizzlingの使用例

Method Swizzlingは主に3つのシナリオで使用されます。監視と分析(自動イベント送信のためのviewDidLoad、viewDidAppearの追跡)、AOPインターセプション(すべてのメソッド呼び出しのパラメータログ記録)、およびホットフィックス(JSPatchなどのライブラリを使用したApp Store Reviewなしでの本番バグ修正)です。

  • 自動分析 — 各コントローラでコードを重複させずに画面表示イベントを送信するためのUIViewController.viewDidAppearのswizzling。
  • ネットワークリクエストログ — サードパーティライブラリも含むすべてのHTTPリクエストを追跡するためのNSURLSession.resumeのswizzling。
  • AOP(アスペクト指向プログラミング) — Aspectsライブラリはメソッドをswizzleし、元の呼び出しの前/後/代わりにコードブロックを実行する。
  • ホットフィックス — アプリケーションを再ビルドせずにバグのあるメソッドの実装を修正済みのものと置き換える(2020年以降App Reviewで禁止)。
  • テストとモック — OCMockは単体テストでメソッドをモック実装に置き換えるためにswizzlingを使用する。

これらの各シナリオは、swizzlingが集中的に適用されるために機能します。分析ライブラリが+loadで一度swizzlingを実行すると、アプリケーション内のすべてのUIViewControllerインスタンスがイベントを送信し始めます。開発者は各コントローラにコードを追加する必要がありません。これにより重複とエラーのリスクが軽減されます。

AndroidでのMethod Swizzling:リフレクションとバイトコード操作

Androidでは、クラシックなObjective-Cの意味でのmethod swizzlingは不可能です。Java/Kotlinはvtableを介した静的ディスパッチを使用します。しかし、同様の効果を達成するメカニズムは存在します。実行時の実装置換のためのJava Reflectionと、ビルド時のバイトコード変更のためのGradle Transform API / ASMです。

kotlin
// Androidでのreflection + 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 Reflectionを介してlog()メソッドの動作を置き換えます。getDeclaredMethodはプライベート実装にアクセスし、isAccessibleはアクセスチェックを無効にします。log()を直接呼び出す代わりに、追加のロジックを実行するラッパーが呼び出されます。ただし、AndroidはJITを介してホットメソッドを最適化するため、リフレクションは既にコンパイルされたAOTセグメントでは機能しない可能性があります。

より信頼性の高いアプローチは、ASMライブラリを使用したGradle Transform APIまたはAGP(Android Gradle Plugin)を介したバイトコード操作です。バイトコードの変更はコンパイル時に行われます。ASMはクラスの各メソッドに呼び出しを追加します。コードカバレッジツール(JaCoCo)やパフォーマンス監視ツール(Firebase Performance Monitoring)はこのように動作します。

Method Swizzlingのリスクとベストプラクティス

Method Swizzlingは高リスクのテクニックです。ライブラリ間の競合:2つのライブラリが同じメソッドをswizzleする場合、実行順序は保証されません。iOSアップデートとの非互換性:Appleが新しいiOSバージョンでシグネチャを変更したりメソッドを削除したりすると、swizzlingはクラッシュを引き起こします。コード内での可視性の欠如:swizzlingはクラス実装に表示されないため、デバッグが複雑になります。

リスク説明軽減策
ライブラリ競合2つのライブラリがviewDidAppearをswizzleすると、一方が他方を壊すclass_getInstanceMethodでメソッドが既にswizzledかどうか確認する
再帰同じメソッドを再swizzlingすると無限ループが発生する常にdispatch_onceを使用する
シグネチャ変更Appleが新しいiOSでメソッドシグネチャを変更する — IMP不一致サポートするすべてのiOSバージョンでテストする
不可視性SwizzlingがXcodeのコールスタックに表示されないコード内のすべてのswizzling操作を文書化する
App ReviewAppleは文書化されていないswizzlingを含むアプリを拒否するパブリックAPIのみを使用し、目的を文書化する

安全なswizzlingのためのベストプラクティスには以下が含まれます:常に元の実装を呼び出す、dispatch_onceを使用して+loadで厳密にswizzlingを実行する、swizzledメソッドにプレフィックス(例:s_originalMethodName)を付ける、各swizzling操作の目的を文書化する。Aspectsライブラリは、元のメソッドの前/後にブロックを連鎖実行することで競合問題を解決します。

最新開発におけるMethod Swizzlingの代替案

Method swizzlingの代替案は、予測可能性と安全性の点から本番コードに推奨されます。デリゲートとプロトコル(UIApplicationDelegate、UITableViewDelegate)は、ランタイムを変更せずに明示的な拡張ポイントを提供します。サブクラス化 — viewDidAppearをオーバーライドするUIViewControllerのサブクラスを作成する — は予測可能に動作し、競合がありません。

SwiftUIとCombineはswizzlingの必要性を排除します。モディファイア(onAppear、onChange)はメソッドをオーバーライドせずに宣言的に動作を追加します。Android Jetpack Composeでは、エフェクト(LaunchedEffect、SideEffect)とモディファイアを通じて同じことを実現します。AOPフレームワーク(Android用AspectJ、iOS用InterposeKit)はコンパイルタイムウィービングによる安全な代替手段を提供します。

Apple WWDC 2024のデータによると、Swiftランタイムは言語レベルでmethod swizzlingをサポートしていません。@objc dynamicメソッドはObjective-C Runtimeを介してのみswizzleできます。@objcを使用しないSwiftアプリケーションは、サードパーティライブラリによる偶発的なswizzlingから完全に保護されています。これによりSwiftはより安全になりますが、ランタイム計装機能は制限されます。

SwiftUIとComposeでの宣言的代替案

SwiftUIモディファイア(onAppear、onChange、onReceive)とJetpack Composeエフェクト(LaunchedEffect、SideEffect、DisposableEffect)は、UIタスクにおいてswizzlingを完全に置き換えます。これらはディスパッチテーブルを変更せずに、横断的な動作を追加する宣言的で予測可能かつテスト可能な方法を提供します。新しいプロジェクトでは、AppleとGoogleはランタイムインターセプションではなくこのアプローチを推奨しています。

よくある質問

Method Swizzlingは本番環境で安全ですか?

Method Swizzlingは、ルールを守れば本番環境で許容されます。dispatch_onceによる1回限りの実行、元の実装の呼び出し、すべてのiOSバージョンでのテスト、および文書化が必要です。単純なタスクでは、デリゲートまたはサブクラス化を使用する方が良いでしょう。本番環境でのSwizzlingは、監視および分析ライブラリに適しています。

SwizzlingとAOPの違いは?

Method Swizzlingはディスパッチテーブル内のIMPを置き換える特定のテクニックです。AOP(アスペクト指向プログラミング)はパラダイムであり、swizzlingはそのメカニズムの1つとして使用されます。AOPにはコンパイルタイムウィービング(AspectJ)、プロキシベースのインターセプション(Spring AOP)、コード生成も含まれます。

Swizzlingによって引き起こされる問題をデバッグするには?

すべてのメッセージを追跡するにはobjc_msgSendにブレークポイントを設定します。クラス名を条件としてmethod_exchangeImplementationsにシンボリックブレークポイントを追加します。FLEXツールはクラスのどのメソッドがswizzledされているかを表示します。体系的なチェックには、クラスのディスパッチテーブルを出力するlldbスクリプトを使用します。

SwizzlingはSwiftで機能しますか?

Swiftは言語レベルでswizzlingをサポートしていません。Method Swizzlingは@objc dynamicとマークされたメソッドでのみ機能し、これらはObjective-C Runtimeを介してコンパイルされます。純粋なSwiftメソッド(@objcなし)は静的ディスパッチを使用するためswizzleできません。それらのディスパッチテーブルは変更のためにアクセスできません。

どのiOSライブラリがSwizzlingを使用していますか?

Firebase Analytics(自動画面追跡のためのviewDidAppearのswizzling)、Amplitude、Mixpanel、FLEX(UI検査)、OHHTTPStubs(ネットワークリクエストモッキング)、Aspects(AOPフレームワーク)。これらはすべて元の実装を呼び出しながら、dispatch_onceを使用して+loadでswizzlingを実行します。

まとめ

  • Method Swizzling — method_exchangeImplementationsを使用してObjective-C Runtimeのディスパッチテーブル内の2つのメソッドのIMPを入れ替える。
  • dispatch_onceは再swizzlingと再帰を防ぐために必須。
  • 元の実装の呼び出しはswizzledメソッド内での必須の安全ルール。
  • Androidでは、swizzlingはGradle Transform / ASMを介したリフレクションまたはバイトコード操作で置き換えられる。
  • リスク — ライブラリ競合、iOSバージョンとの非互換性、デバッガでの不可視性、ホットフィックスのApp Review禁止。
  • 代替案 — デリゲート、サブクラス化、SwiftUIモディファイア、Jetpack Composeエフェクト。
  • @objc dynamicなしのSwiftメソッドはswizzlingから保護されており、安定性は向上するがランタイム計装は制限される。

ターンキー方式のモバイルアプリケーションを開発します

IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。

プロジェクトについて相談

こちらもお読みください