在目标 c 中调用 super.super 上的方法?

Posted

技术标签:

【中文标题】在目标 c 中调用 super.super 上的方法?【英文标题】:Calling a method on super.super in objective c? 【发布时间】:2014-10-02 13:59:24 【问题描述】:

所以我有一个类层次结构如下:

@interface MyClass : ParentClass

-(void)myMethod

    [super myMethod];
    // some specific operation


@end

@interface ParentClass : ParentParentClass

-(void)myMethod

    [super myMethod];
    // some specific operation


@end

@interface ParentParentClass : ParentParentParentClass

-(void)myMethod

    [super myMethod];
    // some specific operation


@end

现在假设,在MyClass 中,我想避免调用ParentClassmyMethod,而是想在ParentParentClass 中调用myMethod

我该怎么做?

我已经尝试投射 super 但这不起作用。

【问题讨论】:

[super [super myMethod]]; 应该可以工作。你遇到了什么错误? @Droppy 我想你的意思是[[super super] myMethod] @PartiallyFinite 我愿意。 你为什么想要这样的东西? @Popeye 这听起来像是一些非常糟糕的设计,这可能是它不可能的原因。 【参考方案1】:

你应该可以这样做:

Method method = class_getInstanceMethod([ParentParentClass class], @selector(myMethod));
IMP imp = method_getImplementation(method);
((void (*)(id, SEL))imp)(self, @selector(myMethod)); // cast the function to the correct signature

您可能需要#import <objc/runtime.h> 才能编译。

这将获取该方法在编译时转换为的实际 C 函数,然后您可以调用该函数。当 Objective-C 编译器编译你的代码时,所有的方法都被转换成普通的 C 函数,它们以 self 作为它们的第一个参数,_cmd,当前方法的选择器作为第二个参数,然后是所有的Objective-C 方法采用的其他参数。 class_getInstanceMethod 获取给定方法的运行时表示(包括各种元数据),method_getImplementation 从该方法获取纯 C 函数指针。

如果查看 Objective-C 运行时标头,您会看到 IMP 类型定义为 typedef id (*IMP)(void);,因此您需要将其强制转换为方法实现函数的实际类型,即(return_type (*)(id, SEL, method_arguments_in_order)) — 该函数将 self 和方法选择器作为其前两个参数,然后是 ObjC 方法参数。

所以,一旦你有了标准的 C 函数指针,你就可以像调用函数一样简单地调用它。

我不会将这种方法称为 hacky,但它肯定是非标准,因为需要使用底层运行时这一点很清楚方法直接实现你想要的。就设计、可靠性和意义而言,我肯定会认为这是一个更好的解决方案,而不是在超类中添加调用其超类方法的桥接方法。

【讨论】:

@P.Sami 这很老套。非常hacky。必须在 super 的 super 中调用方法是糟糕设计的明确迹象。它也很脆弱,充满了问题。也就是说,它假设 super 的实现可以被绕过,从而破坏了封装。 @bbum 我理解/知道这违反了封装原则,但有时你必须做你必须做的事情......很高兴知道有一种技术可以到达 super.super ;) @bbum 我更多地指的是这样一个事实,即这可能是解决 hacky 问题的最少 hacky 可能的解决方案。 @P.Sami 请注意,我们实际上并没有像这样检索super.super,我们只是直接检索您通过名称指定的特定类提供的实例方法实现。 【参考方案2】:

super 不是一个对象,它是一个特殊的关键字,它告诉编译器发出对objc_msgSendSuper 的调用,而不是objc_msgSend

由于没有objc_msgSendSuperSuper这样的功能,你想要的就做不到了。

您将不得不依赖具有不同选择器的方法。

【讨论】:

【参考方案3】:

这确实是一个非常糟糕的做法, 无论如何,如果你需要这样做,你需要做一个桥接方法来调用

@interface MyClass : ParentClass

-(void)myMethod

    // INVOKE BRIDGE
    [super myMethodSuperBridge];
    // some specific operation


@end

@interface ParentClass : ParentParentClass

-(void)myMethod

    [super myMethod];
    // some specific operation


- (void)myMethodSuperBridge

    [super myMethod];

@end

@interface ParentParentClass : ParentParentParentClass

-(void)myMethod

    [super myMethod];
    // some specific operation

【讨论】:

【参考方案4】:

你可以这样做

(void(*)(struct objc_super *, SEL, id))objc_msgSendSuper)(&(struct objc_super)self, [[self superclass] superclass], @selector(myMethod));

也许你想调用 super.super.super 方法,你可以这样做

(void(*)(struct objc_super *, SEL, id))objc_msgSendSuper)(&(struct objc_super)self, [[[self superclass] superclass] superclass], @selector(myMethod));

【讨论】:

以上是关于在目标 c 中调用 super.super 上的方法?的主要内容,如果未能解决你的问题,请参考以下文章

面向对象中super的作用

super关键字

super().__repr__() 和 repr(super()) 有啥区别?

super关键字

super()方法详解

java 第33节 实现继承