ObjcRuntimeのいくつかの妙用について話します(class_addMethod,class_replaceMethod)


前言:今日お話しする知識点を陳列します:class_addMethod,class_replaceMethod,method_getImplementation,object_getClass
に関する知識
』』categoryを用いてRuntimeにより自己の関数で元の関数を入れ替える
』ocのmessage forwarding
』』Runtimeを使用してクラスに元にないメソッドを追加
』』』なぜcategoryで書き直さないのか
この文章の中の技術の参考はもちろん四方八方から来て、異なる時期から来て、弟はただ総括をして、悪いところがあってみんなの指導を歓迎します
まず一つのシーンの問題から持ち出しましょう.卒業設計の時、弟はipadアプリを作って、後になってから回転スクリーンを加えることにしました.100以上のファイルを見ています.20以上のページはもう少しで血を吐かないところでした.ハハ、コントロールごとに修正する方法は不可能です.強迫症も親を作りたくないので、これらのコントロールのviewWillAppearとw i l n i m ateRotationToInterfaceOrientation:duration:を一度に置き換えることにしました.
まずcategoryを見てみましょう
classを運用することでaddMethodとclass_replaceMethodシステムライブラリを交換する方法
[objc]  view plain
 copy
#import "NSObject+Swizzle.h"  
  
@implementation NSObject (Swizzle)  
  
+ (BOOL)swizzleMethod:(SEL)origSel withMethod:(SEL)aftSel {  
      
    Method originMethod = class_getInstanceMethod(self, origSel);  
    Method newMethod = class_getInstanceMethod(self, aftSel);  
      
if(originMethod&&newMethod){//両方のMethodがを手に入れなければならない
        if(class_addMethod(self, origSel, method_getImplementation(newMethod), method_getTypeEncoding(newMethod))) {  
//追加成功後            class_replaceMethod(self, aftSel, method_getImplementation(originMethod), method_getTypeEncoding(originMethod));  
        }  
        return YES;  
    }  
    return NO;  
}  
@end  
1.2つのパラメータを入力し、元のメソッド選択子、新しいメソッド選択子、class_を介してgetInstanceMethod()対応するMethodを入手
2.class_addMethodは、実装に対して、本来、操作するClassに存在しないnewMethodの実装を操作されるClassに追加する、origSelをその選択子として用いる(パラメータのselfは操作されるClassであることに注意し、ここではクラスメソッドであることを忘れない).
3.class_replaceMethod,addMethodが正常に完了した後、パラメータからmethod_を交換することを目的としていることがわかります.getImplaementation(roiginMethod)のセレクタは、元のメソッドの実装のSELを新しいメソッドのSEL:aftSelに置き換え、ok目的が達成された.考えてみれば、今は古い方法SELで呼び出すと、新しい方法のIMPが実現し、新しい方法のSELで呼び出すと、古い方法のIMPが実現し、理一理の考えが続きます.
今回はNSStringをキャリアとしてプレゼンテーションしていきましょう.
[objc]  view plain
 copy
#import "MyString.h"  
#import "NSObject+Swizzle.h"  
  
@implementation MyString  
  
+ (void)load {  
    static dispatch_once_t onceToken;  
    dispatch_once(&onceToken, ^{  
        Class clazz = object_getClass((id)self);  
        [clazz swizzleMethod:@selector(resolveInstanceMethod:) withMethod:@selector(myResolveInstanceMethod:)];  
    });  
}  
  
+ (BOOL)myResolveInstanceMethod:(SEL)sel {  
      
    if(! [self myResolveInstanceMethod:sel]) {  
        NSString *selString = NSStringFromSelector(sel);  
        if([selString isEqualToString:@"countAll"] || [selString isEqualToString:@"pushViewController"]) {  
            class_addMethod(self, sel, class_getMethodImplementation(self, @selector(dynamicMethodIMP)), "v@:");  
            return YES;  
        }else {  
            return NO;  
        }  
    }  
    return YES;  
}  
  
- (void)dynamicMethodIMP {  
NSLog(@「動的に加えた関数です」);}  
  
@end  
1.まずここでresolveInstanceMethodについてお話しします.知らない人はocのmessage forwardingを補うことができます.つまり、実行時にオブジェクトが見つからない方法を呼び出したときにシステムが探すメカニズムです.この方法は最初に行った場所です.ここでruntimeに方法を追加することができます.はい、まずこの方法をハイジャックしなければなりません.私たち自身のことをして、さっきcategoryにカプセル化されたswizzleMethod:withMethod:
------この时、友达に质问がありました.私たちはこの方法を书いて自分のことをすることができます.実はできません.categoryで既存の方法を书くと警告があります.Category is implementing a method which will also be implemented by its primary class、このようなやり方は提唱されません.
---------categoryはサブクラスの代わりにはできません.サブクラスのようにsuperによって親クラスを呼び出す方法では実現できません.categoryで現在のクラスを上書きするメソッドを書き換えると、この現在のクラスの元のメソッドが実装され、実行されません.これはいくつかのメソッドで致命的です(ここでは、現在のメソッドで実行してからcategoryで実行する特例+(void)loadを示します).
---------2つのcategoryが同じメソッドを書き換えた場合、どの優先度が高いかを制御することはできません.従来は継承による書き換え方法を提唱してきました
2.object_getClassは現在のMyStringのClassを手に入れて、さっきcategoryにカプセル化されたswizzleMethod:withMethod:を呼び出して、私たちのmyResolveInstanceMethod:で原生のものを置き換えて、よし、今私たちが実行中に存在しない方法を呼び出したら、システムは私たちのmyを呼び出します.
ResolveInstanceMethod:はい、疑う必要はありません.
3.今myResolveInstanceMethodを見てみましょう.myResolveInstanceMethodをもう一度呼び出します.原生的な方法の実現に変わりました、ok.if(![self myResolveInstanceMethod:sel])は、一度に入力されたメソッドが存在するかどうかを検出するために、オリジナルメソッドを呼び出す実装であり、まだ存在しない場合はclass_addMethodオペレーションは、システムによって呼び出され、OKされ、目的を達成するreturn YESのような対応するメソッドを追加します.
------ここに知識点classを追加します.addMethodパラメータの意味は、クラス--選択子--実装--メソッドの戻り値とパラメータ資料の順です.vは戻り値voidを表し、@はidタイプオブジェクトを表し、:選択子を表す.なぜかというと、それぞれのocメソッドには2つの暗黙的なパラメータ(id self,SEL_cmd)があり、C言語関数に2つのパラメータを加えて1つのocメソッドを構成しているとも言える.OK、疑問はまた解決しました.
最後に私たちの仕事の収穫を見てみましょう.
[objc]  view plain
 copy
NSLog(@"begin test");  
//---------------------------------------------------------------  
      
    MyString *string = [[MyString alloc] init];  
    [string performSelector:@selector(countAll)];  
    [string performSelector:@selector(pushViewController)];  
[objc]  view plain
 copy
//---------------------------------------------------------------  
    NSLog(@"finish test");  
-----Log:
[javascript]  view plain
 copy
2015-10-29 18:12:56.815 ObjcRuntimeDemo[8875:563683] begin test  
2015-10-29 18:12:56.815 ObjcRuntimeDemo[875:563683]動的に加わった関数です
2015-10-29 18:12:56.815 ObjcRuntimeDemo[875:563683]動的に加わった関数です
2015-10-29 18:12:56.815 ObjcRuntimeDemo[875:563683]動的に加わった関数です
2015-10-29 18:12:56.815 ObjcRuntimeDemo[8875:563683] finish test