Objetive-Cのメソッドパッケージ(Method Swizzling)


テキストリンク:http://nshipster.com/method-swizzling/
メソッド・パッケージ(Method Swizzling)は、あるSEL(既存の実装)を変更する方法実装に適用される.このテクノロジーは、OCのメソッド呼び出しを実行時にSELとクラス発表の関数マッピング関係を変更することで、本当に呼び出したい関数を指定します.
栗を挙げると、XXアプリケーションの各ビューコントローラが何回も弾き出されていることを統計します.「ユーザー行動統計...一般的には..」.
統計コードをviewDidAppear:に追加する必要があります.これにより、トンの重複コードが発生する可能性があります.継承は別のソリューションかもしれませんが、さまざまな継承が必要です.UIViewController, UITableViewController, UINavigationControllerです.同時に、コードの重複を避けることはできません.
幸いなことに、カテゴリ内のメソッドパッケージ(method swizzling)という別のソリューションがあります.コードは次のとおりです.
#import 

@implementation UIViewController (Tracking)

+ (void)load {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        Class class = [self class];

        // When swizzling a class method, use the following:
        // Class class = object_getClass((id)self);

        SEL originalSelector = @selector(viewWillAppear:);
        SEL swizzledSelector = @selector(xxx_viewWillAppear:);

        Method originalMethod = class_getInstanceMethod(class, originalSelector);
        Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);

        BOOL didAddMethod =
            class_addMethod(class,
                originalSelector,
                method_getImplementation(swizzledMethod),
                method_getTypeEncoding(swizzledMethod));

        if (didAddMethod) {
            class_replaceMethod(class,
                swizzledSelector,
                method_getImplementation(originalMethod),
                method_getTypeEncoding(originalMethod));
        } else {
            method_exchangeImplementations(originalMethod, swizzledMethod);
        }
    });
}

#pragma mark - Method Swizzling

- (void)xxx_viewWillAppear:(BOOL)animated {
    [self xxx_viewWillAppear:animated];
    NSLog(@"viewWillAppear: %@", self);
}

@end

次に、任意のUIViewControllerのインスタンスまたはサブクラスがviewWillAppear:を呼び出すと、LOGが印刷される.
【注意:用途】ビューコントローラのライフサイクルに動作、イベント応答者、図面、またはネットワーク層を注入することは、メソッドパケットの使用場所です.(もちろん使用シーンも多く、OC開発者にとっても必須です).
どうやって遊ぶべきかを見続けます.
+load vs. +initialize
パケットの処理はずっと+loadで行われるべきだ.
OC実行時に自動的に呼び出される方法は2つあります.1つは+loadが各クラスが最初にロードされたときであり、もう1つは+initializeがこのクラスまたはこのインスタンスを最初に呼び出す方法を適用したときである.いずれもオプションで、呼び出されたメソッドが実装されている場合にのみ実行されます.
メソッドパケットはグローバル状態に影響するため,衝突の確率を最小化することが重要である.+loadは、クラス初期化中にロードされることを保証し、システム範囲を変更する動作において少しの一貫性を提供する.逆に、+initializeは、実行されるときにこのような保障がなく、実際には、アプリケーションがこのクラスに直接メッセージを送信しない場合、呼び出されない可能性があります.
dispatch_once
パケットは常にdispatch_onceで処理されるべきである
メソッド・パケットの影響がグローバルであるため、実行時に予想できる予防措置が必要であることを再宣言します.原子性は、スレッド間でもコードが一度だけ実行されることを保証する予防である.GCDのdispatch_onceは、原子性と実行の一意性を提供します.
Selectors, Methods, & Implementations
OCでは、selectorsmethodsおよびimplementationsは、ほとんどのシーンでこれらのものが互いに通じるが、通常はメッセージ送信のプロセスを指す実行時に比較的特殊な領域に関連する.
アップルの公式説明を引用します.
  • Selector (typedef struct objc_selector *SEL): Selectors are used to represent the name of a method at runtime. A method selector is a C string that has been registered (or "mapped") with the Objective-C runtime. Selectors generated by the compiler are automatically mapped by the runtime when the class is loaded .
  • Method (typedef struct objc_method *Method): An opaque type that represents a method in a class definition.
  • Implementation (typedef id (*IMP)(id, SEL, ...)): This data type is a pointer to the start of the function that implements the method. This function uses standard C calling conventions as implemented for the current CPU architecture. The first argument is a pointer to self (that is, the memory for the particular instance of this class, or, for a class method, a pointer to the metaclass). The second argument is the method selector. The method arguments follow.


  • これらの概念を最もよく理解する方法は、クラス(Class)が実行時にメッセージ配信を処理するために1枚の分割発表を維持し、テーブル内の各エントリは、固有の名前、すなわちセレクタ(SEL)から特定の実装(IMP)、すなわちC関数へのポインタをマッピングする方法(Method)である.
    パッケージの1つの方法は、SELとIMPのマッピング関係を変えることです.【ASEL->AIMP BSEL->BIMPパッケージ後のASEL->BIMP BSEL->AIMP】
    呼び出し_cmd次のコードはデッドサイクルを引き起こすようです.
    - (void)xxx_viewWillAppear:(BOOL)animated {
        [self xxx_viewWillAppear:animated];
        NSLog(@"viewWillAppear: %@", NSStringFromClass([self class]));
    }
    

    しかし、不思議ですね.まさかそうではありません.パッケージの処理では、xxx_viewWillAppear:UIViewController -viewWillAppear:の元の実装に再割り当てされました.再帰呼び出しは問題になりますが、このシーンでは問題ありませんよ.実行時にxxx_viewWillAppear:が本当に実装されたので、パッケージされています.(意訳)
    ここにはコード仕様があり、パケットメソッドの名前に接頭辞を付けることを覚えています.同じように他のカテゴリにあります.
    思索する
    パッケージは魔術技術(黒魔法)として認識されており、最も安全ではありませんが、以下の法則に従えば、安全性は受け入れられます.
  • Always invoke the original implementation of a method (unless you have a good reason not to)
  • Avoid collisions
  • Understand what's going on
  • Proceed with caution

  • まとめはMethod Swizzzlingが使いやすいけど少なめに...