objective-C中方法转换的正确方法

objective-C中方法转换的正确方法,objective-c,objective-c-runtime,swizzling,method-swizzling,Objective C,Objective C Runtime,Swizzling,Method Swizzling,目前正在试验Objective-C中的方法swizzling,我有一个问题。我试图理解正确的方法swizzle,在网上研究后,我偶然发现了这篇NSHipster帖子: 在这篇文章中,作者给出了一些使用示例代码的方法。我正在找人更好地向我解释作者在做什么。。我尤其对didAddMethod逻辑感到困惑。为什么作者不直接交换/交换方法实现?关于这一点,我唯一的理论是,视图可能会出现:尚未添加到UIViewController的方法中。特别是如果在UIViewController之前先将类别加载到内

目前正在试验Objective-C中的方法
swizzling
,我有一个问题。我试图理解正确的方法swizzle,在网上研究后,我偶然发现了这篇NSHipster帖子:


在这篇文章中,作者给出了一些使用示例代码的方法。我正在找人更好地向我解释作者在做什么。。我尤其对
didAddMethod
逻辑感到困惑。为什么作者不直接
交换/交换
方法实现?关于这一点,我唯一的理论是,
视图可能会出现:
尚未添加到
UIViewController的方法中。特别是如果在
UIViewController
之前先将类别加载到内存中。。。这就是原因吗?这似乎很奇怪?只是想了解更多的细节/清晰度,谢谢:)

在obj-c运行时添加
类时调用加载

因此,假设在obj-c运行时中添加了一个
UIViewController
,该运行时已经包含
view,那么它将出现:
,但您希望它被另一个实现替换。因此,首先添加一个新方法
xxx将出现:
。 现在,一旦
xxx出现:
已添加到
ViewController
类中,只有这样您才能替换它

但作者也说:

例如,假设我们想要跟踪每个视图控制器在iOS应用程序中呈现给用户的次数

因此,他试图演示这样一种情况,即一个应用程序可能有许多视图控制器,但您不希望继续为每个
ViewController
更换
视图:
实现。一旦出现
视图点:
已被替换,则不需要添加,只需进行交换

目标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;
}
如果需要,您可以挖掘更多:

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

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

首先忽略这个例子,这个例子是关于特定类的一个类别:代码> UIViewController < /代码>,只考虑逻辑,好像类别在任意的类上,我们把这个类称为代码> TargetClass < /C> > 我们将调用要替换的现有方法

existingMethod

该类别位于
TargetClass
上,将swizzling方法(我们称之为
swizzlingMethod
)添加到
TargetClass

重要:请注意,获取方法的函数
class\u getInstanceMethod
将在提供的类或其任何超类中找到该方法。但是,函数
class\u addMethod
class\u replacethod
仅在提供的类中添加/替换方法

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

  • TargetClass
    本身直接包含现有方法的实现。这是一个简单的例子,所有需要做的就是交换
    现有方法
    swizzlingMethod
    的实现,这可以通过
    方法交换实现来完成。在本文中,对
    class\u addMethod
    的调用将失败,因为直接在
    TargetClass
    中已经存在
    existingMethod
    ,并且逻辑导致调用
    method\u exchangeimplements

  • TargetClass
    不直接包含
    现有方法的实现,而是通过继承
    TargetClass
    的一个祖先类来提供该方法。这是一个更棘手的案例。如果您只是简单地交换
    existingMethod
    swizzlingMethod
    的实现,那么您将影响祖先类的(实例)(并且以一种可能导致崩溃的方式-为什么留作练习)。通过调用
    class\u addMethod
    文章的代码确保
    TargetClass
    中存在
    现有方法
    ——其实现是
    swizzlingMethod
    的原始实现。然后,该逻辑将
    swizzlingMethod
    的实现替换为祖先的
    现有方法的实现(这对祖先没有影响)

  • 还在这儿吗?我希望这是有道理的,而不是简单地让你生气

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


    HTH

    答案很简单,这一切都很有意义。你的锻炼建议也很好,我将深入探讨:)你似乎对跑步非常了解,我刚刚发布了另一个问题。如果你有机会看一看(如果你有时间的话),那就太棒了。谢谢你,伙计!