オーバーライドした後、カテゴリ内の事前定義されていないメソッドを呼び出す

継承を使用するよりもクラスにカテゴリを記述する方が適切な場合があります。 また、継承でsuperを呼び出したかのように、カテゴリ内で元のクラスメソッドを使用する必要がある場合もあります。 時々彼らはインタビューでそのような機会について尋ねることを好み、何らかの理由でこれができないことを確信しています。 Googleもこの可能性について沈黙しています。 誰が気にします-猫へようこそ。



実際、それはすべて、古い既存のプロジェクトで右から左に書くことで、言語のサポートから始まりました。 翻訳の最初の犠牲者はUITableViewクラスでした。 すべてがほぼ継承されて機能するようになった後、このタイプのレターのサポートを他のプロジェクトに迅速に追加するためのカテゴリを適用することにしました。



右から左への文字のサポートは、いくつかのテーブルメソッドの再デプロイで構成されていました。 この記事では、 initWithCoder:メソッドについて説明します。



まず、 UITableViewのカテゴリを作成しましょう



@interface UITableView (RTL) @end @implementation UITableView (RTL) - (id)initWithCoder:(NSCoder *)aDecoder{ //self = - ; if (self) { // -  } return self; } @end
      
      





テーブルを何かで初期化する必要があります。最も適切な方法は、元のinitWithCoder:メソッドを呼び出すことです。 しかし、以前に話したように、これは不可能です。 私はこれを信じないことに決めましたが、 Method * class_copyMethodList(クラスcls、unsigned int * outCount)関数を使用してクラスで何が起こっているのかを見ることにしました。 そこで、事前定義されたメソッドの実装が2回発生するのを見ました。 したがって、これらの実装のアドレスを取得して、どちらにいるのかを確認すると、もう一方の呼び出しが必要になります。 必要な実装を検索するために、次のクラスメソッドが作成されました。



 - (objc_methodPointer)methodBySelector:(SEL)sel{ unsigned int mc = 0; Class selfClass = object_getClass(self); Method *mList = class_copyMethodList(selfClass, &mc); Method currentMethod = class_getInstanceMethod(selfClass, sel); NSString *oldName = NSStringFromSelector(sel); Method oldMethod = NULL; for(int i = 0;i < mc; i++){ NSString *name = [NSString stringWithUTF8String:sel_getName(method_getName(mList[i]))]; if ([name isEqualToString:oldName] && currentMethod != mList[i]){ oldMethod = mList[i]; break; } } return oldMethod; }
      
      





ここではすべてが単純で、検索用のセレクターが関数に渡されます。 それから、名前と現在のメソッドを取得します。 次に、メソッドのリストを調べて、必要な実装を探し、名前とメソッドの不一致の対応を確認します。



これで、必要なメソッドへのポインタができましたが、その実装を知る必要があります。 幸いなことに、それは受け取った構造に保存されます。 しかし、何らかの理由で、Appleは完全なメソッド構造をprepファイルに入れないことを決定しましたが、developer.apple.comにはその説明があります。 したがって、すべてを自分で説明する必要があります。



 struct objc_method { SEL method_name; char *method_types; IMP method_imp; }; typedef struct objc_method *objc_methodPointer;
      
      





これで、元の初期化メソッドを呼び出すすべてができました。



 - (id)initWithCoder:(NSCoder *)aDecoder{ //      static objc_methodPointer m = NULL; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ m = [self methodBySelector:_cmd]; }); //     if (m){ //    self = (void *)m->method_imp(self, m->method_name, aDecoder); if (self) { //     } } return self; }
      
      





このコードを使用してアプリケーションをビルドおよび開始すると、テーブルの基本的な初期化が実行されます。 その後、私たちのものが実行されます。



このコードは、オリジナルで理想的なふりをするものではありません。これは今後数日のうちに完成する最初のバージョンです。 (今でも、メソッドのリストのコピーが呼び出されていますが、メモリは解放されていません)。



ご清聴ありがとうございました。



All Articles