Objective-Cメソッドのスウィズリングのベストプラクティス

Objective-CメソッドスウィズリングのベストプラクティスObjective-Cでのメソッドスウィズリング
は非常に強力なテクノロジーであり、メソッドの実装を動的に置き換え、サブクラス化よりも柔軟なフック関数を実装できます。」 「オーバーライド」メソッド。

メソッドスウィズリングの原理

メソッドスウィズリングは両刃の剣です。適切に使用すると、複雑な機能を非常に簡単に実装でき、誤用するとプログラムに壊滅的なダメージを与える可能性があります。しかし、慌てる必要はありません。実装の原則を理解すれば、「すぐに利用できる」ようになります。

まず、Objective-Cのメソッドのデータ構造を理解しましょう。

typedef struct method_t *Method;
struct method_t {
    SEL name;
    const char *types;
    IMP imp;

    struct SortBySELAddress :
        public std::binary_function<const method_t&,
                                    const method_t&, bool>
    {
        bool operator() (const method_t& lhs,
                         const method_t& rhs)
        { return lhs.name < rhs.name; }
    };
};

本質的に、これはstruct method_t型のポインターであるため、構造method_tの定義に焦点を当てます。3つのメンバー変数と1つのメンバー関数が構造method_tで定義されています。

1.nameはメソッドの名前を表し、@ selector(viewWillAppear :)など、メソッドを一意に識別するために使用されます
。2.typesは、メソッドの戻り値とパラメータータイプを表します(詳細については、Appleの公式ドキュメントのタイプを参照してください)。エンコーディング);
3.impは、メソッドの実装を指す関数ポインターです。4.SortBySELAddressは
、名前が示すように、名前のアドレスに従ってメソッドをソートする関数です。

このことから、我々はまた、Objective-Cの中のメソッド名には、次の2つの方法が実行時に同じ方法であると思われることを意味し、パラメータの種類、含まれていないことを見つけることができます
:-(無効)viewWillAppear:(BOOL)はアニメーションを
;-( void)viewWillAppear:(NSString *)string;
 ただし、次の2つのメソッドを共存させることができます
:-( void)viewWillAppear:(BOOL)animated;
+(void)viewWillAppear:(BOOL)animated;

メソッドスウィズリングの用途は何ですか

@interface UIViewController (MRCUMAnalytics)

@end

@implementation UIViewController (MRCUMAnalytics)

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

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

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

        BOOL success = class_addMethod(class, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod));
        if (success) {
            class_replaceMethod(class, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));
        } else {
            method_exchangeImplementations(originalMethod, swizzledMethod);
        }
    });
}

#pragma mark - Method Swizzling

- (void)mrc_viewWillAppear:(BOOL)animated {
    [self mrc_viewWillAppear:animated];
    [MobClick beginLogPageView:NSStringFromClass([self class])];
}

@end

分析:上記のコードには、注意が必要な3つの重要なポイントがあります。

+ initializeなどの他のメソッドの代わりに+ loadメソッドにメソッドSwizzlingのロジックが実装されているのはなぜですか。
メソッドSwizzlingのロジックをdispatch_onceでディスパッチする必要があるのは
なぜですか。class_addMethodメソッドを呼び出してその結果に基づいて行う必要があるのはなぜですか2つの異なる状況に別々に対処します。
これら3つが何のためにあるのかを分析しましょう。

最初の理由:前回の記事「Objective-C + load vs + initialize」を読んだ学生は、+ loadと+ initializeがObjective-Cランタイムが自動的に呼び出す2つのクラスメソッドであることを知っておく必要があります。ただし、これらは異なる時間に呼び出されます。+ loadメソッドはクラスがロードされるときに呼び出され、+ initializeメソッドはクラスまたはそのサブクラスが最初のメッセージを受信する前に呼び出されます。インスタンスメソッドとクラスメソッドの呼び出しを含むメッセージを参照します。つまり、+ initializeメソッドはレイジーロード方式で呼び出されます。プログラムが特定のクラスまたはそのサブクラスにメッセージを送信していない場合、このクラスの+ initializeメソッドは呼び出されません。さらに、+ loadメソッドには非常に重要な機能があります。つまり、サブクラス、親クラス、およびカテゴリでの+ loadメソッドの実装は異なる方法で処理されます。つまり、Objective-Cランタイムによって+ loadメソッドが自動的に呼び出された場合、カテゴリ内の+ loadメソッドはメインクラスの+ loadメソッドを上書きしません。要約すると、+ loadメソッドは、メソッドスウィズリングロジックを実装するのに最適な「場所」です。

2番目の理由:前述のように、+ loadメソッドは、クラスがロードされるときにランタイムによって1回自動的に呼び出されますが、プログラマーが+ loadメソッドを手動で呼び出すことを制限しません。何?あなたはプログラマーがこれをしないと言いましたか?たぶん、viewDidLoadメソッドを手動で呼び出すプログラマーも見たことがありますが、彼らはとても気まぐれです。私たちにできることは、プログラムが可能な限りさまざまな条件下で正常に実行できるようにすることです。

3番目の理由:メソッドスウィズリングを使用する目的は、通常、特定の機能を完全に置き換えるのではなく、プログラムに機能を追加することです。したがって、通常、カスタム実装で元の実装を呼び出す必要があります。したがって、個別に対処する必要がある2つの状況があります。

ケース1:メインクラス自体に、置き換える必要のあるメソッドがあります。つまり、class_addMethodメソッドはNOを返します。この状況の処理は比較的簡単で、2つのメソッドの実装を直接交換するだけです。

  • (void)viewWillAppear:(BOOL)animated { ///最初に元の実装を呼び出します。メインクラス自体がこのメソッドを実装するため、ここでの実際の呼び出しはメインクラスの実装です[self mrc_viewWillAppear:animated]; ///追加しました関数[MobClickbeginLogPageView:NSStringFromClass([self class])]; }




  • (void)mrc_viewWillAppear:(BOOL)animated { ///メインクラスの実装}ケース2:メインクラス自体は、置き換える必要のあるメソッドを実装していませんが、親クラスの実装を継承しています。つまり、class_addMethodメソッドはYESを返します。このとき、class_getInstanceMethod関数を使用して取得したoriginalSelectorは親クラスのメソッドを指し、class_replaceMethod(class、swizzledSelector、method_getImplementation(originalMethod)、method_getTypeEncoding(originalMethod));を実行して、親クラスの実装をカスタムmrc_viewWillAppearに置き換えます。方法。これにより、mrc_viewWillAppearメソッドの実装で親クラスを呼び出すという目的が達成されます。


  • (void)viewWillAppear:(BOOL)animated { ///メインクラス自体はこのメソッドを実装していないため、最初に元の実装を呼び出します。これが親クラスの実装です[self mrc_viewWillAppear:animated]; ///追加します[MobClick beginLogPageView:NSStringFromClass([self class])]; }の関数




  • (void)mrc_viewWillAppear:(BOOL)animated { ///親クラスの実装}ここを参照してください。メソッドSwizzlingについてはすでにある程度理解していると思いますので、自分で試してみてください。自分で試してみてください。


おすすめ

転載: blog.csdn.net/wangletiancsdn/article/details/97650349