【问题标题】:Override a method via ObjC Category and call the default implementation?通过 ObjC 类别覆盖方法并调用默认实现?
【发布时间】:2010-11-08 07:12:31
【问题描述】:

使用类别时,您可以使用自己的方法覆盖实现方法,如下所示:

// Base Class 
@interface ClassA : NSObject 
- (NSString *) myMethod;
@end
@implementation ClassA
- (NSString*) myMethod { return @"A"; }
@end

//Category
@interface ClassA (CategoryB) 
- (NSString *) myMethod;
@end
@implementation ClassA (CategoryB)
- (NSString*) myMethod { return @"B"; }
@end

在包含类别后调用方法“myMethod”会得到结果“B”。

myMethod的Category实现调用原始A类myMethod最简单的方法是什么?据我所知,您必须使用低级调用来获取 A 类的原始方法挂钩并调用它,但似乎有一种语法上更简单的方法来做到这一点。

【问题讨论】:

    标签: objective-c


    【解决方案1】:

    如果你想要一个 hackish 的方式来做到这一点,包括使用 Objective-C 运行时,你总是可以使用method swizzling(在此处插入标准免责声明。)它将允许你将不同的方法存储为任意命名的选择器,然后在运行时根据需要交换它们。

    【讨论】:

    • 这不一定是“hackish”方式。 Objective-C 运行时的存在是有原因的。这就是使 Objective-C 优于其他编译语言的原因。
    • 根据我的经验,切入正题,您想使用 JRSwizzle,如上面的链接中所述。这对我来说是防弹的。
    • 链接到 JRSwizzle 代码:github.com/rentzsch/jrswizzle ......我已经开始喜欢这个了,只要你想调整 3rd 方库以随心所欲地做,方法混合就非常强大!
    【解决方案2】:

    来自comp.lang.objective-C FAQ listing:“如果多个类别实现相同的方法会怎样?那么我们所知道的宇宙结构将不复存在。实际上,这并不完全正确,但肯定存在一些问题当一个类别实现了一个已经出现在一个类中的方法时(无论是通过另一个类别,还是该类的主要@implementation),该类别的定义会覆盖之前存在的定义。原来的定义不能再被由 Objective-C 代码达到。请注意,如果两个类别覆盖相同的方法,则最后加载的“获胜”,这在代码启动之前可能无法预测。”

    来自developer.apple.com:“当一个类别覆盖一个继承的方法时,该类别中的方法可以像往常一样通过消息调用继承的实现给super。但是,如果一个类别覆盖了一个已经存在于category 的类,无法调用原来的实现”

    【讨论】:

    • 是的,该语言确实不支持。
    • 感谢您的评论,但我正在寻找它是如何做到的(因为我知道它可以做到,只是不容易)而不是它是不可能的......
    • 我认为super 应该解析为原始对象。毕竟,类别很像子类(例如,您可以覆盖方法)。
    • 在超类Category中声明实现的Category方法中重写是否正确?
    • 我可能不需要说,但以防万一,对于未来的读者:一个类别真的一点也不像一个子类。 super 不会解析为“原始对象”。
    【解决方案3】:

    查看我关于在 Mac 开发人员库中找到的解决方案的文章: http://codeshaker.blogspot.com/2012/01/calling-original-overridden-method-from.html

    基本上和上面的 Method Swizzling 是一样的,只是一个简单的例子:

    #import @implementation 测试(记录) - (NSUInteger)logLength { NSUInteger 长度 = [self logLength]; NSLog(@"记录:%d", 长度); 返回长度; } +(无效)加载{ method_exchangeImplementations(class_getInstanceMethod(self, @selector(length)), class_getInstanceMethod(self, @selector(logLength))); } @结尾

    【讨论】:

      【解决方案4】:

      使用 ConciseKit 中包含的 swizzling "helper" 方法,您实际上调用了默认实现……很奇怪.. 通过调用您的 SWIZZLED 实现..

      你在+ (void) load中设置,调用+ (BOOL)swizzleMethod:(SEL)originalSelector with:(SEL)anotherSelector in:(Class)klass;,即

      [$ swizzleMethod:@selector(oldTired:) 
                  with:@selector(swizzledHotness:) in:self.class];
      

      然后在 swizzled 方法中.. 假设它返回 -(id).. 你可以做你的恶作剧,或者任何你一开始就 swizzling 的原因......然后,而不是返回一个对象,或者 self , 或者诸如此类..

      return [self swizzledHotness:yourSwizzledMethodsArgument];
      

      As explained here…

      在这个方法中,看起来我们再次调用同一个方法,导致无限递归。但是当到达这条线时,这两种方法已经交换了。因此,当我们调用 swizzled_synchronize 时,我们实际上是在调用原始方法。

      感觉和看起来很奇怪,但是.. 它有效。这使您能够为现有方法添加无尽的装饰,并且仍然“调用超级”(实际上是自我)并获得原始方法的手工的好处......即使没有访问原始源。

      【讨论】:

        猜你喜欢
        • 2018-09-14
        • 2019-01-16
        • 1970-01-01
        • 1970-01-01
        • 2014-07-07
        • 1970-01-01
        • 2011-11-03
        • 1970-01-01
        • 2014-11-20
        相关资源
        最近更新 更多