【问题标题】:Override a method in a single object instance覆盖单个对象实例中的方法
【发布时间】:2011-06-02 11:51:35
【问题描述】:

我不知道该怎么说,我找不到答案,因为我无法找到表达我所寻找的词语。 (!)

在Java中,我曾经做过这样的事情(我不记得了):

JPanel myButton = new JPanel("Press me"){
    public void add(JComponent component){
        //override add method
    }
};

但是,我在 Objective-C 中找不到如何做到这一点。我在搜索中发现的是类别和奇怪的 ^{} 符号...

那么,如何在新创建的对象中覆盖方法?

(例如,在新创建的NSString* 中覆盖-(BOOL)isEqual; ?)

如果问题有点含糊,我很抱歉..

编辑:

显然,没有子类化:)

编辑:

如果有人有更好的主意,不妨发布我的问题:

我在 COCOS2D 中有几个 CCTransition,我希望在过渡结束时得到通知。问题是,一旦过渡结束,-(void)finish;方法被调用(它是 CCTransition 类结构的一部分)

我真的很想避免继承 CCTransition 类,并在转换结束时重写完成方法来执行我的逻辑:)

编辑:

-(void)onEnterTransitionDidFinish; ...我不敢相信有这么棒的东西存在,而且我在搜索时没有遇到过......

这意味着,不是继承 CCTransition,而是在我的 CCNode 子类中重写此方法:D!

【问题讨论】:

  • 您要覆盖类中的方法还是只覆盖一个对象(类实例)?
  • 我要回答使用子类。我不知道其他方式..
  • 这在Java中是非常可能和方便的。我认为objective-C更加通用?
  • 它可以在运行时完成,你甚至可以根据需要从字符串编译方法,但为什么不使用子类呢?这正是子类的用途。
  • 我最终将子类化大约 10 个类.. CCTransitionFadeIn、CCTransitionSlideIn、...等。而且每个人都有自己的逻辑。可维护性/可读性对我来说是一个非常重要的要求。

标签: objective-c overriding


【解决方案1】:

它仍然不会很干净,但假设你愿意集中丑陋,你可以做类似的事情(未经测试):

Method methodToReplace =
          [targetClass instanceMethodSignatureForSelector:@selector(methodToReplace)];
IMP implementationToSet =
          [someProxyClass instanceMethodForSelector:@selector(implementationYouWant)];

method_setImplementation(methodToReplace, implementationToSet);

相关的参考文档是 Objective-C Runtime Reference 和可选的 NSObject Class Reference(因为它使一些事情稍微整洁,尽管您可以在运行时使用 class_getInstanceMethod 而不是 instanceMethodSigntureForSelector:)。

请注意,如果您完全使用该配方,您将无法调用原始实现。 method_setImplementation 返回旧实现,通常明智的做法是将其添加到全新选择器下的类中并调用它。

作为参考,我有正当理由只做一次这种事情:当我们在需要与 OS 3.2 和 4.0 兼容的 iOS 应用程序中实现打印支持时。您需要对特定类进行子类化,但该类在 3.2 中不可用。所以你必须在运行时进行子类化(虽然概念上更简洁的方法是使用普通的子类,将其放入框架和弱链接中,但 Apple 的 iOS SDK 条款只允许静态库,所以......)。

【讨论】:

  • 这是需要运行时创建的子类的有趣情况。我想知道是否有一种稍微简单的方法是在程序执行的早期在运行时动态创建一个虚拟超类,以便您可以正常定义子类。那还能用吗?
  • 我在这里的知识水平处于边缘,但我认为潜在的问题是与我的子类相关的元类将在加载二进制文件时由系统实例化。由于那时父类不存在,我想我只会得到一个异常并且根本无法启动。
  • 看来你是对的:+load 甚至在 main() 之前运行。但是,according to Mike Ash,类别的 +load 方法与主类的实现一起执行,因此您可以将 hacky 的东西放在 +[NSObject load] 上的类别中...
  • 他提到首先加载框架并且 +load 是一种特殊情况(因为一个类可以有多个 +loads 并且都被调用),我想问题是类别是否 +load将在加载框架后调用,尽管 NSObject 是加载框架所必需的。如果是这样,那么您是对的 - 一个几乎可以肯定有效的更简洁和更好的解决方案是在运行时添加一个虚拟父类,然后完全实现我的打印子类,就好像我只针对 4.0 一样。如果没有别的,当我们放弃 3.2 支持时,它就更少了。
【解决方案2】:

按照 Daniel 的建议,您可以在表单的 NSObject 类别中实现一个方法

[anObject overrideMethod:@selector(foo:) 
          byBlock:^(id self,id super,id originalArg){
          ...
}];

你需要做的是

  1. objc_allocateClassPairself 自己的类,创建一个新的临时类
  2. 将块转换为函数指针,例如使用thisthis
  3. method_setImplementation 将新实现设置为临时类
  4. 使用object_setClassself 将类设置为新的临时类
  5. 我还没有想出如何将super 提供给块:p

据信这基本上是 Apple 完成 KVO 的方式,参见例如this discussion.

阅读Runtime reference

【讨论】:

  • 这会很酷。我想不出任何方法来提供super,但您可能会编写一个方法或函数来调用父方法的实现,例如-(id)callSuperMethod:(SEL)selector withObject:(id)arg1 ...
  • 好吧,我猜你可以写一个蹦床方法,将方法发送给超级,如:[[self super] doSomething:...]
【解决方案3】:

您在 Java 中拥有的是一个匿名子类。这在 Objective-C 中是不可能的(嗯,它有点像,但你必须对 Obj-C 运行时库做一些相当复杂的扭曲)。

但从 iOS 4 或 OS X 10.6 开始,Objective-C 具有“块”,这就是 ^{} 语法的用途。这是 Objective-C 的 closure 概念。如果您调用的 API 不支持块回调,这不会直接帮助您,但您可以创建使用块而不是子类方法来处理回调的包装类。

many resources可以学习Objective-C中的blocks。

【讨论】:

  • 啊,匿名内部类 .. 你让我想起了 Java 的美好时光 :) ... 是的,这就是我需要的答案 “你在 Java 中拥有的是一个匿名子类。这在 Objective-C 中是不可能的” .. 谢谢!
猜你喜欢
  • 2011-04-21
  • 1970-01-01
  • 1970-01-01
  • 2012-07-19
  • 1970-01-01
  • 2023-02-09
  • 1970-01-01
  • 1970-01-01
  • 2012-04-24
相关资源
最近更新 更多