2009-08-22 9 views
10

class_addMethodでクラスに追加されたメソッドを削除することはできますか?Objective-C 2.0で実行時にインスタンスメソッドを削除するにはどうすればよいですか?

これをやりたければ、実行時にobjc_allocateClassPairを使用してクラスを作成し続け、異なるメソッドセットを追加して実装されたメソッドを変更する必要がありますか?

私はハッカーを含む回答を受け入れるでしょう:-)

答えて

10

要するに、あなたはできません。経由でのObjective-C 1.0 ABI/APIで

あなたは可能性:

OBJC_EXPORT void class_removeMethods(Class, struct objc_method_list *) OBJC2_UNAVAILABLE; 

しかし、メソッドを削除すると、かなり正しい答えになることはありませんので、その機能は、Objective-Cの2.0で削除されました。確かに、この機能をサポートすることによって発生するオーバーヘッドを正当化するのに十分ではない。

また、ObjC2.0 ABIから削除されたのは、クラス/メソッド構造に直接アクセスする能力でした。現在は不透明であるため、バイナリ互換性を損なうことなく将来変更することができます。

しかし、できることは、それが応答するメソッドのセットを変更するカスタムプロキシを使用することです。 NSProxyクラスのドキュメントを参照してください。 http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSProxy_Class/Reference/Reference.html

もちろん、この質問は「あなたは何をしようとしていますか?そのようなフライメタプログラミングは、非典型的です。クラスがインスタンス化されると、以前のインスタンス化が依然として上記のメソッドに依存する可能性があるという仮定の下で、それが応答するメソッドのセットを変更することは通常望ましいとは考えられません。

+0

「なぜ」という答えは、KVCが使用されたときの状況を示すコードを書くことに興味があることです。実行時にメソッドを追加したり削除したりすることで、メソッドのさまざまな組み合わせが存在するという効果をユーザーが探検できるようになりました。 私は、これを行うことができるのは、内部についてのことだけであると結論づけました。 Objective-C 2.0は、ハッカーを除いてすべての手段で停止しています。 –

+0

少し遅いかもしれませんが、おそらくあなたのサンプルに、あなたが望むメソッドに応答する実行時に動的にサブクラスを作成することができます。メソッドを削除する代わりに、そのメソッドを持たない基本クラスの新しいサブクラスを作成するだけで済みます。 –

+0

メソッドの削除が必要な別のケース:単体テストでは、既存のクラスのメソッドをスタブアウトする必要があることがあります。 OCMockフレームワークではこれを行うことができますが、OCMock自体ではメソッドを削除できないため、個々のテストの後にクラスを復元​​するのは困難です。既存のメソッドのIMPをスタブに置き換えることはできますが、そのメソッドが親クラスにあった場合は、そのサブクラスにスタブを追加して削除する必要があります。だから、あなたは何をしようとしているのですか?質問:私は完全に "懐疑的なソフトウェア"を得るが、私の意見では、Appleのランタイムはそれであってはならない。 –

13

メソッドを正確に「削除」することはできませんが、すべてをメッセージ転送パス(最終的には-forwardInvocation:を呼び出すコード)にリダイレクトするだけで、削除と同じ効果を得ることができます。それを達成するには、2つの方法があります。

  1. _objc_msgForward()/usr/include/objc/message.hで宣言されたが、オンラインドキュメントに含まれていない、あなたが削除しようとしているあなたの方法のためのIMPとして使用することができます。文書化されていないため、非公開またはサポートされていないと見なされる可能性があります。
  2. class_getMethodImplementation()は、存在しないことがわかっているセレクタで呼び出すことができ、削除しようとしているメソッドにはIMPという結果を使用できます。ランタイムの代わりに実際のメソッドの実装に内部関数であってもよい戻さ

関数ポインタ:ドキュメントに基づいて、これは、最初のオプションと同じ効果を有していなければなりません。たとえば、クラスのインスタンスがセレクタに応答しない場合、返される関数ポインタはランタイムのメッセージ転送機構の一部になります。いずれの場合も

、それはのように簡単になる:任意のスーパークラスは、有効なを持っている可能性がある場合は、より注意する必要がありますので、これは基本的に、任意のスーパークラスの実装を遮断することを

method_setImplementation(methodToRemove, forwardingIMP); 

は注意あなたが保持したい実装。そのような場合は、スーパークラスまたはそれに類するものからIMPを得ることができます。