6

swizzling現在、Objective-Cでメソッドを試していますが、質問があります。私はスウィズルをメソッド化する適切な方法を理解しようとしており、オンラインで調査した後、この NSHipster の投稿に出くわしました :

投稿では、著者はサンプルコードを入れ替えるメソッドをいくつか持っています。著者が何をしているのかをよりよく説明してくれる人を探しています。特に、didAddMethod論理について混乱しています。なぜ著者は直接swapping/exchangingメソッドの実装だけではないのですか? これに関する私の唯一の理論は、まだviewWillAppear:追加されていない可能性があるということですUIViewController's method dispatch_table。特に、カテゴリが最初にメモリにロードされている場合はUIViewController...これが理由ですか? かなり奇妙に思えますか?もう少し洞察/明確さを探しているだけです、ありがとう:)

4

2 に答える 2

7

特に、didAddMethod ロジックについて混乱しています。メソッドの実装を直接スワップ/交換するだけではないのはなぜですか?

このロジックは明確に説明されていないため、混乱は理解できます。

最初に、例が特定のクラスのカテゴリであるという事実を無視し、UIViewControllerそのカテゴリが任意のクラスにあるかのように論理を考えて、そのクラスを呼び出しましょうTargetClass

置き換えたい既存のメソッドを呼び出しますexistingMethod

on であるカテゴリTargetClassは、swizzling メソッドを に追加しswizzlingMethodますTargetClass

重要: メソッドを取得する関数 は、指定されたクラスまたはそのスーパークラスのいずれかでclass_getInstanceMethodメソッドを見つけることに注意してください。ただし、関数と提供されたクラスのメソッドのみを追加/置換します。class_addMethodclass_replaceMethod

ここで、考慮すべき 2 つのケースがあります。

  1. TargetClass自体には の実装が直接含まれていますexistingMethod。これは簡単なケースです。実行する必要があるのは、 と の実装を交換するexistingMethodことだけです。swizzlingMethodこれは で実行できますmethod_exchangeImplementations。この記事では、 への呼び出しclass_addMethodは失敗します。これは、既に がexistingMethod 直接含まれて TargetClassおり、ロジックによって が呼び出されるためmethod_exchangeImplementationsです。

  2. TargetClassの実装を直接含むのではなくexistingMethod、 の祖先クラスの 1 つからの継承によってメソッドが提供されTargetClassます。これはよりトリッキーなケースです。の実装を単純に交換するとexistingMethodswizzlingMethod祖先クラス (のインスタンス) に影響を与えることになります (そして、クラッシュを引き起こす可能性のある方法で - なぜ演習として残されています)。class_addMethod記事のコードを呼び出すことで、 の元の実装であるexistingMethodinが存在することを確認します。次に、ロジックは の実装を祖先の実装に置き換えます(祖先には影響しません)。TargetClassswizzlingMethodswizzlingMethodexistingMethod

まだここ?それが理にかなっていて、単にあなたを斜視に送ったわけではないことを願っています!

最終的に興味がある場合の別の演習: 祖先の実装に...へのexistingMethod呼び出しが含まれているとどうなるかを尋ねるかもしれません。同じメソッドの実装が 2 回実行される祖先への実装か、それとも本来の意図どおりの祖先の祖先への実装か?superswizzlingMethodTargetClasssuper

HTH

于 2015-12-29T01:56:41.233 に答える
1

loadclassaが obj-c ランタイムに追加されたときに呼び出されます。

https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSObject_Class/#//apple_ref/occ/clm/NSObject/load

したがって、UIViewControllerすでに含まれている obj-c ランタイムに a が追加されviewWillAppear:たが、それを別の実装に置き換えたいとします。まず、新しいメソッドを追加しますxxxWillAppear:。一度クラスにxxxWillAppear:追加されViewControllerたら、それを置き換えることができます。

しかし、著者は次のようにも述べています。

たとえば、iOS アプリで各ビュー コントローラーがユーザーに提示された回数を追跡したいとします。

ViewControllerそのため、彼は、アプリに多くのビュー コントローラーが含まれる可能性があるが、viewWillAppear:実装ごとに置き換え続けたくないケースを実証しようとしています。の点viewWillAppear:が置き換えられたら、追加する代わりに、交換のみを行う必要があります。

おそらく、Objective C ランタイムのソース コードが役立つかもしれません。

/**********************************************************************
* addMethod
* fixme
* Locking: runtimeLock must be held by the caller
**********************************************************************/
static IMP 
addMethod(Class cls, SEL name, IMP imp, const char *types, BOOL replace)
{
IMP result = nil;

rwlock_assert_writing(&runtimeLock);

assert(types);
assert(cls->isRealized());

method_t *m;
if ((m = getMethodNoSuper_nolock(cls, name))) {
    // already exists
    if (!replace) {
        result = _method_getImplementation(m);
    } else {
        result = _method_setImplementation(cls, m, imp);
    }
} else {
    // fixme optimize
    method_list_t *newlist;
    newlist = (method_list_t *)_calloc_internal(sizeof(*newlist), 1);
    newlist->entsize_NEVER_USE = (uint32_t)sizeof(method_t) | fixed_up_method_list;
    newlist->count = 1;
    newlist->first.name = name;
    newlist->first.types = strdup(types);
    if (!ignoreSelector(name)) {
        newlist->first.imp = imp;
    } else {
        newlist->first.imp = (IMP)&_objc_ignored_method;
    }

    attachMethodLists(cls, &newlist, 1, NO, NO, YES);

    result = nil;
}

return result;
}


BOOL 
class_addMethod(Class cls, SEL name, IMP imp, const char *types)
{
if (!cls) return NO;

rwlock_write(&runtimeLock);
IMP old = addMethod(cls, name, imp, types ?: "", NO);
rwlock_unlock_write(&runtimeLock);
return old ? NO : YES;
}


IMP 
class_replaceMethod(Class cls, SEL name, IMP imp, const char *types)
{
if (!cls) return nil;

rwlock_write(&runtimeLock);
IMP old = addMethod(cls, name, imp, types ?: "", YES);
rwlock_unlock_write(&runtimeLock);
return old;
}

必要に応じて、さらに掘り下げることができます。

http://www.opensource.apple.com/source/objc4/objc4-437/

于 2015-12-29T00:28:13.260 に答える