objective c categories and inheritance
如果一个方法同时在该类的类和类别中定义,则不定义将调用哪个实现。
但是,这与继承是如何相互作用的呢?明确地:
- 给定一个超类类别方法和子类中的一个正则方法,当调用子类的一个成员时,是否保证子类实现会获胜?
- 给定一个超类正则方法和一个试图重写它的子类类别方法,当调用子类的成员时,是否保证子类类别实现会获胜?
- 给定一个超类类别方法和一个子类类别方法,当调用子类的一个成员时,是否保证子类类别方法会获胜?
- 我不会说"不可能"。您可以在Category+Load方法中捕获原始实现,并享受一些乐趣;)
- 如果类别重写了类别类中存在的方法,则将调用类别方法。当同一类上的两个类别实现/重写同一方法时,就会发生冲突。这一点很重要,因为许多cocoa类的方法都是按类别实现的。如果您试图重写一个框架定义的方法,它可能是在一个类别中实现的,而哪个实现优先是未定义的。
我们就这么说吧。不要重写使用类别、句点、Ever和答案结尾的方法。
- 糟糕的是,我今天没有投票支持这件事。
- 我认为人们喜欢理性,而不是盲目的建议
- @熵:在这个问题被问了上百次之后,乔舒亚的回答完全符合需要。类别并不是继承和子类化的工具。
If a method is defined in both a class and a category on that class,
it is undefined which implementation will be called.
这是不正确的;类别方法将永远获胜。但是,不起作用的是,如果有多个类别实现了相同的方法,那么"哪一个赢"就没有定义。
这通常是"最后一场胜利",但这也不是一个硬性的规则。
请注意,由于许多类的内部实现将为代码组织的目的跨类别划分,因此您无论如何都不能依赖第一个规则。
简而言之,约书亚所说的;不要使用类别覆盖方法。
除了继承的原因之外,当您这样做时,您还恶意地破坏了封装。基于类别的实现并不是重写现有的方法,而是完全替换它。因此,如果您不重现每一个最后的内部实现细节,包括bug,那么您的替换将不会很好地工作,并且调试将非常困难。
- 嗯,这是我问题的一部分。超类(uiview)是Apple类。我有一个类别添加了一个方法;让我们称之为myuiviewCategoryMethod。如果我在子类中重写这个方法,我能确定子类版本会赢吗?如果我在子类上使用一个类别呢?
- 是的——子类实现总是会赢。
- 子类化并不总是可用的,但有时需要替换或增强某些功能。在这种情况下,不建议使用类别覆盖。相反,使用目标C运行时将您自己方法的实现与您希望修改的方法交换,并在适当的情况下调用原始实现。
- @莱昂纳坦不,不,不…答案不是"嗖"的一声。如果你的应用程序刷新了系统方法,那是违反应用程序存储策略的。有充分的理由,因为它通常会导致脆弱、崩溃的代码,这些代码可能在任何给定的平台或更新上表现出新的、完全未定义的行为。
- 请不要告诉我我的代码"通常"会导致什么,因为事实并非如此。请给我指一下应用商店的"政策",它是反对的。目标C运行时之所以是公共API,是有原因的,并且直到人们充分地接受它(包括ISA对uiWebDocumentView的Swizing)。;)它们没有使用提供给它们的整个特性集。也没有人指定你来决定什么样的解决方案适合哪里。
- 有时答案是"急流"。
- @实际上,系统类中的Swizing方法是脆弱的,会导致崩溃和/或未定义的行为。虽然在Swizzled实现中的某个地方调用原始实现当然可以降低风险,但它仍然以破坏封装并导致脆弱性的方式更改系统提供的实现。在我编写、维护和调试大型Objective-C应用程序的二十多年中,我非常相信方法Swizzling是一个有趣的黑客,不应该在生产代码中使用。
- 请参阅此问题:stackoverflow.com/questions/8834294/…,因为它包含了一个由于系统API的旋转而导致应用程序存储拒绝的示例。
- 似乎是一种反复无常的拒绝。我同意使用dealloc方法可能是错误的,我必须承认我已经使用了.cxx析构函数来调试特殊的问题。我强烈反对"娱乐黑客"。目标C运行时作为一个整体将被使用。更重要的是,基金会和可可使用的核心概念和ISA扫描提供KVO、外观代理、核心数据功能等。
- @Leonatan您可以自由使用Swizing或任何您想要实现自己功能的机制。使用相同的机制修改系统提供的类会带来麻烦(和拒绝,正如该问题所证明的那样)。仅仅因为一个工具可以服务于一个特定的功能并不意味着使用一个工具来完成这个功能是合适的;我可以用锤子来驱动一个螺钉,但是我的橱柜会很糟糕。
- @我想说你不知道怎么用锤子。什么是"系统提供的类"?粉底和可可?它们没有任何系统,只是苹果提供的框架,它们使用"系统提供的"和"用户提供的"类和方法进行类似的"欺骗"(非常误导性的词,因为这里没有任何技巧)。
- 来自DOCS的LeoNatan:"本地应用程序是使用IOS系统框架和Objtovi-C语言构建的。"所以,是的,基金会/可可/ UIKIT/CORDATA(以及其他由苹果交付的框架)都是"系统框架",而您的代码不应该通过该框架的公共API来修改它们的行为。Swizzling和直接使用ivar没有什么不同;它会破坏封装、脆弱,并导致应用程序崩溃。为了增添一点幽默感,uikit团队留下了一个注释:openradar.appspot.com/7044974
- @bbum所以"系统框架"可以破坏封装,脆弱,导致应用崩溃?对不起,不知道你对苹果开发者有什么看法,但他们也是人,可能有缺陷。从你戏剧性的陈述中,听起来目标C框架是下一个"goto"。拜托。对错误和崩溃的恐惧不应该让你害怕可行的选择,它应该驱使你拥有更好的代码。
- 让我们在聊天中继续讨论
- @事实上,戴夫和巴布姆与称你为巨魔一点关系都没有——我对你的许多观点发表了一篇评论,并说我通常不会回应"巨魔和其他在线骚扰我的人";你不是巨魔,只是你回应人们的方法让我很不舒服。这是我的咆哮,再一次,与你所说的相反:pastebin.com/enalznse。很明显,这个讨论是脱题的(这就是为什么所有评论都被删除的原因),但我只是不想让你认为Dave或Bbum会堕落到这个地步。
- @托达说得更清楚了。我再次向戴夫和巴布道歉。你的咆哮实际上比我在邮件预览中看到的更有建设性。我很乐意回答并进行辩论(我同意某些事情,但我不同意),但由于这场讨论被认为是非主题,遗憾的是,我不会。我将在主题上说,在某些情况下,快速旋转比类别更安全,因为类别没有受到那么多的关注,但有更大的可能引起问题,特别是使用具有相同名称的未暴露方法。
- @莱昂纳坦当然了-很抱歉发表这样一个愤怒的咆哮,你可能不应该得到它。不管怎样,是的,有些情况下,旋转比分类更安全,但这些情况通常是远远的,很少。在大多数情况下,如果遵循良好的命名准则(将前缀放在类别中的方法名称上),可能永远不会遇到命名冲突。无论如何,???????????????????!)
根据我的测试
给出了超类分类方法和正则方法子类,是否保证子类实现将获胜?当调用子类的成员时?=>子类获胜
给出了一个超类正则方法和一个子类分类方法试图重写它,是否保证子类类别当调用子类的成员时,实现将获胜?= >子类类别获胜
给出了一个超类分类方法和一个子类分类方法,是否保证子类类别方法在调用子类的成员?=>子类类别获胜
看看测试类别和子类
- 当涉及到类别覆盖时谈论子类对我来说已经是错误的了。相信bbum,他可能是这个主题中栈溢出最有能力的人之一。
- @维金戈塞贡多你说得对。我只想通过一个小测试来澄清这一点。运行时添加类别方法的顺序必须与
- 就像bbum说的:很多课程都分为不同的类别。我们不知道他们的订单。
- @所以如果我们知道方法是在类本身中实现的,我们当然可以用类别覆盖它们,对吗?
- 正如bbum指出的那样:您必须重新创建覆盖的系统类方法的每个行为,因为您不能仅仅调用其他实现。其中包括您现在没有的实现细节。如果你开始改写你自己的方法,那就表明你的设计有缺陷。