18

私は NSObject のカテゴリを持っています。オブジェクトでそれを呼び出すとき、その dealloc メソッドをオーバーライドしてクリーンアップを行いたいと考えています。

メソッドスウィズリングを使用してやりたかったのですが、方法がわかりませんでした。私が見つけた唯一の例は、クラス全体のメソッド実装を置き換える方法に関するものです (私の場合、すべての NSObject の dealloc をオーバーライドしますが、これは望ましくありません)。

NSObject の特定のインスタンスの dealloc メソッドをオーバーライドしたいと考えています。

@interface NSObject(MyCategory)
-(void)test;
@end

@implementation NSObject(MyCategory)
-(void)newDealloc
{
  // do some cleanup here
  [self dealloc]; // call actual dealloc method
}
-(void)test
{
  IMP orig=[self methodForSelector:@selector(dealloc)];
  IMP repl=[self methodForSelector:@selector(newDealloc)];
  if (...)   // 'test' might be called several times, this replacement should happen only on the first call
  {
     method_exchangeImplementations(..., ...);
  }
}
@end
4

3 に答える 3

20

オブジェクトには独自のメソッド テーブルがないため、実際にはこれを行うことはできません。クラスのみがメソッド テーブルを持ち、それらを変更すると、そのクラスのすべてのオブジェクトに影響します。ただし、これを回避する簡単な方法があります。実行時にオブジェクトのクラスを動的に作成されたサブクラスに変更します。isa-swizzling とも呼ばれるこの手法は、Apple が自動 KVO を実装するために使用します。

これは強力な方法であり、用途があります。しかし、あなたの場合、関連オブジェクトを使用するより簡単な方法があります。基本的にobjc_setAssociatedObject、別のオブジェクトを最初のオブジェクトに関連付けるために使用し、そのオブジェクトでクリーンアップを行いますdealloc詳細については、Cocoa is my Girlfriend に関するこのブログ投稿を参照してください。

于 2012-02-29T18:34:42.130 に答える
12

メソッドの選択はオブジェクト インスタンスのクラスに基づいているため、メソッドの入れ替えは同じクラスのすべてのインスタンスに影響します。

ただし、インスタンスのクラスを変更することはできますが、注意が必要です! クラスがあると仮定して、概要を次に示します。

@instance MyPlainObject : NSObject

- (void) doSomething;

@end

のインスタンスの一部についてMyPlainObject動作を変更したい場合はdoSomething、最初にサブクラスを定義します。

@instance MyFancyObject: MyPlainObject

- (void) doSomething;

@end

のインスタンスを明確に作成できるようになりましたが、既存の のインスタンスを取得して に作成し、新しい動作を取得するMyFancyObject必要があります。そのために、クラスをスウィズルできます。以下を に追加します。MyPlainObjectMyFancyObjectMyFancyObject

static Class myPlainObjectClass;
static Class myFancyObjectClass;

+ (void)initialize
{
   myPlainObjectClass = objc_getClass("MyPlainObject");
   myFancyObjectClass = objc_getClass("MyFancyObject");
}

+ (void)changeKind:(MyPlainObject *)control fancy:(BOOL)fancy
{
   object_setClass(control, fancy ? myFancyObjectClass : myPlainObjectClass);
}

の元のインスタンスに対して、MyPlainClassとして動作するように切り替えることができますMyFancyClass。また、その逆も可能です。

MyPlainClass *mpc = [MyPlainClass new];

...

// masquerade as MyFancyClass
[MyFancyClass changeKind:mpc fancy:YES]

... // mpc behaves as a MyFancyClass

// revert to true nature
[MyFancyClass changeKind:mpc: fancy:NO];

(いくつかの) 注意事項:

サブクラスがメソッドをオーバーライドまたは追加し、(クラス) 変数を追加する場合にのみ、これを行うことができます。static

また、動作を変更したいクラスごとにサブクラスが必要です。多くの異なるクラスの動作を変更できる単一のクラスを持つことはできません。

于 2012-02-29T18:38:15.997 に答える
1

インスタンス固有のスウィズリングも備えたスウィズリング API を作成しました。これはまさにあなたが探しているものだと思います: https://github.com/JonasGessner/JGMethodSwizzler

実行時にスウィズルしている特定のインスタンスの動的サブクラスを作成することで機能します。

于 2013-10-27T22:08:25.370 に答える