如何在调用super的代码中重构双重性

时间:2017-11-14 10:41:24

标签: cocoa refactoring

我有两个NSResponder方法(剪切,复制),除了调用自己的超级代码之外,它们的代码基本相同。如何使用参数_CMD创建方法作为调用super的选择器,我不会以递归结束?

- (void)copy:(id)sender
{
    [self notifyAndPerformSelector:_cmd withObject:sender];
}

- (void)cut:(id)sender
{
    [self notifyAndPerformSelector:_cmd withObject:sender];
}

- (void)notifyAndPerformSelector:(SEL)selector withObject:(id)sender
{
    [super performSelector:selector withObject:sender];
    //code...
}

1 个答案:

答案 0 :(得分:1)

正如您所发现的,您的代码不会根据需要调用超类方法,而是调用当前类中的超类方法,从而导致无限递归。

您面临的第一个选择是重构您的代码,类似于:

@implementation MyDerivedClass
{
   - (void)copy:(id)sender
   {
       [super copy:sender];
       [self commonCodeAfterSelector:_cmd withObject:sender];
   }

   - (void)cut:(id)sender
   {
       [super cut:sender];
       [self commonCodeAfterSelector:_cmd withObject:sender];
   }

   - (void)notifyAndPerformSelector:(SEL)selector withObject:(id)sender
   {
       //code...
   }
}

如果这种方法适合您的情况,请使用它。如果不是......

第二个选择是成为编译器......

标准和超级方法调用

表格的标准方法调用:

[object someMethodWithArg1:x andArg2:y]

为方法someMethodWithArg1:andArg2:调用搜索。此搜索从object运行时类开始。强调运行时很重要,object引用的实际对象可以与object 的声明类型属于同一类,并且该类型的子类和搜索必须找到方法的最派生实现。

表单的超级方法调用:

[super someMethodWithArg1:x andArg2:y]

还为方法someMethodWithArg1:andArg2:调用搜索。但是,此搜索从代码发生的类的超类编译时类开始。例如,如果上面的MyDerivedClassMyBaseClass的子类,那么搜索方法将从MyBaseClass 忽略运行时类型self - 其中可以是MyDerivedClass或其子类(比如MyDerivedDerivedClass

为什么您当前的代码会递归?

您的电话:

[super performSelector:selector withObject:sender];

开始搜索超类中的方法performSelector:withObject:,该搜索在到达NSObject类之前不会找到该方法。找到方法后,调用该方法并启动标准(不是 super )搜索selector的方法,此搜索从运行时类型{{1开始所以在self ...递归中找到方法。

您需要的是:

MyDerivedClass

但不幸的是,这不存在。但你可以做一个......

编译方法调用

编译器采用以下形式的标准方法调用:

[self performSuperSelector:selector withObject:sender];

并且有效地(我们正在掩盖一些细节,需要填写下面的内容)将其编译为对运行时函数[object someMethodWithArg1:x andArg2:y] 的调用:

objc_msgSend()

请注意,选择器作为objc_msgSend(object, @selector("someMethodWithArg1:andArg2:"), x, y) 值传递,这是SEL的值来源。

表单的超级调用:

_cmd

有效地编译为对[super someMethodWithArg1:x andArg2:y] 形式的调用:

objc_msgSendSuper()

您可以直接在自己的代码中调用这些运行时函数。您必须导入objc_msgSendSuper(`struct` containing `self` and superclass, @selector("someMethodWithArg1:andArg2:"), x, y) 以获取定义,将它们转换为适当的类型等。

成为编译器并绕过<objc/objc-runtime.h>

您的代码使用performSelector,因为它具有performSelector值,但如上所示,用于方法调用的运行时调用直接使用SEL。如果您自己“编译”超级调用,则不需要使用SEL,这反过来可以避免递归问题。

在调用performSelector之前,需要强制转换函数,使其返回和参数类型与您调用的选择器的实际返回和参数类型相匹配。这样就可以编译正确的代码来处理参数和返回值,并且该代码依赖于类型。您调用的两个选择器objc_msgSendSuper()copy:具有相同的类型,这使得代码更短。为了使铸造更容易,我们首先定义类型的简写:

cut:

typedef void (*CutOrCopyRunner)(struct objc_super *super, SEL op, id sender); 定义为函数指针类型。现在你的方法:

CurOrCopyRunner

HTH