在objective-C中正确的方法冲洗方式



当前在Objective-C中尝试方法swizzling,我有一个问题。我正在努力理解swizzle方法的正确方法,在网上研究后,我偶然发现了NSHipster的这篇帖子:http://nshipster.com/method-swizzling/

在这篇文章中,作者有一些方法来改写示例代码。我正在找人更好地向我解释作者在做什么。。特别是我对didAddMethod逻辑感到困惑。为什么作者不直接用swapping/exchanging方法实现呢?我唯一的理论是,viewWillAppear:可能还没有被添加到UIViewController's method dispatch_table中。特别是如果类别可能在UIViewController之前先加载到内存中。。。这就是为什么吗?这看起来很奇怪?只是想获得更多的见解/清晰度,谢谢:)

特别是我对didAddMethod逻辑感到困惑。为什么作者不直接交换方法实现?

你的困惑是可以理解的,因为这个逻辑没有解释清楚。

首先忽略这个例子是特定类UIViewController上的一个类别这一事实,只考虑该类别在某个任意类上的逻辑,让我们将该类称为TargetClass

我们将调用现有的方法来替换existingMethod

TargetClass上的类别将swizzling方法(我们称之为swizzlingMethod)添加到TargetClass中。

重要:请注意,获取方法的函数class_getInstanceMethod将在提供的类或其任何超类中找到该方法。然而,函数class_addMethodclass_replaceMethod仅添加/替换所提供类中的方法。

现在有两种情况需要考虑:

  1. CCD_ 17本身直接包含CCD_。这是一个简单的情况,所需要做的就是交换existingMethodswizzlingMethod的实现,这可以用method_exchangeImplementations来完成。在本文中,对class_addMethod的调用将失败,因为直接在TargetClass中已经存在和existingMethod,并且逻辑导致对method_exchangeImplementations的调用。

  2. TargetClass不直接包含existingMethod的实现,而是通过继承TargetClass的祖先类之一来提供该方法。这是更棘手的情况。如果您简单地交换existingMethodswizzlingMethod的实现,那么您将影响祖先类(的实例)(并且在某种程度上可能导致崩溃-为什么保留为练习)。通过调用class_addMethod,本文的代码确保TargetClass中有一个existingMethod,它的实现是swizzlingMethod的原始实现。然后,逻辑将swizzlingMethod的实现替换为祖先的existingMethod的实现(这对祖先没有影响)。

还在这里?我希望这是有道理的,而不是简单地让你斗鸡眼!

如果你非常好奇,还有一个练习:现在你可能会问,如果祖先的existingMethod实现包含对super的调用,会发生什么。。。如果实现现在也附加到TargetClass中的swizzlingMethod,那么对super的调用将在哪里结束?它是对祖先中的实现,将看到相同的方法实现执行两次,还是对祖先的祖先,如最初的意图?

HTH

load是在obj-c运行时中添加class时调用的。

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

因此,假设在obj-c运行时中添加了一个UIViewController,该运行时已经包含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/

相关内容

  • 没有找到相关文章

最新更新