在目标 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
中,我想避免调用ParentClass
的myMethod
,而是想在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 上的方法?的主要内容,如果未能解决你的问题,请参考以下文章