57

有人可以向我解释一下 Objective C 中类别和继承之间的区别吗?我已经阅读了 Wikipedia 中的条目,并且关于类别的讨论看起来与继承没有任何不同。我也看了《Open iPhone Development》一书中关于这个话题的讨论,还是没看懂。

4

7 回答 7

96

有时,继承似乎比它的价值更麻烦。当您想向现有类添加一些改变该类行为的东西时,它是正确使用的。

使用类别,您只希望现有对象多做一点。如前所述,如果您只想拥有一个处理压缩的字符串类,则不需要对字符串类进行子类化,只需创建一个处理压缩的类别。这样,您无需更改已使用的字符串类的类型。

线索在于类别仅添加方法的限制,您不能使用类别将变量添加到类中。如果该类需要更多属性,则必须对其进行子类化。(编辑:我相信您可以使用关联存储)。

类别是添加功能的好方法,同时符合面向对象的原则,优先选择组合而不是继承。

编辑 2012 年 1 月

现在情况发生了变化。使用当前的 LLVM 编译器和现代的 64 位运行时,您可以将 iVar 和属性添加到类扩展(而不是类别)。这使您可以将私有 iVar 排除在公共界面之外。但是,如果您为 iVar 声明属性,它们仍然可以通过 KVC 访问/更改,因为在 Objective-C 中仍然没有私有方法之类的东西。

于 2009-02-07T01:52:16.260 回答
17

类别允许您向现有类添加方法。因此,您可以直接将它们添加到 NSData 类中,而不是子类 NSData 来添加您时髦的新加密方法。您应用程序中的每个 NSData 对象现在都可以访问这些方法。

要了解这有多么有用,请查看:CocoaDev

于 2009-02-06T22:03:19.500 回答
11

NSString 是 Objective-c 类别中最喜欢的示例之一。NSString 是在 Foundation 框架中定义的,它没有视图或窗口的概念。但是,如果您在 Cocoa 应用程序中使用 NSString,您会注意到它会响应类似– drawInRect:withAttributes:.

AppKit 为 NSString 定义了一个类别,它提供了额外的绘图方法。该类别允许将新方法添加到现有类中,因此我们仍然只是处理 NSStrings。如果 AppKit 通过子类化来实现绘图,我们将不得不处理“AppKitStrings”或“NSSDrawableStrings”或类似的东西。

类别允许您将应用程序或特定领域的方法添加到现有类。它可以非常强大和方便。

于 2009-12-09T05:37:41.217 回答
4

如果您作为程序员获得了代码库或应用程序的完整源代码集,那么您可以发疯并更改您需要的任何内容,以使用该代码实现您的编程目标。

不幸的是,情况并非总是如此,甚至是不可取的。很多时候,你会得到一个二进制库/对象工具包和一组头文件。

然后一个类需要一个新功能,所以你可以做几件事:

  1. 创建一个新类整体而不是一个普通类——复制它的所有函数和成员,然后重写所有代码以使用新类。

  2. 创建一个新的包装类,其中包含股票类作为成员(合成)并重写代码库以利用新类。

  3. 库的二进制补丁来更改代码(祝你好运)

  4. 强制编译器将您的新类视为旧类,并希望它不依赖于特定大小或内存中的位置和特定入口点。

  5. 子类专业化——创建子类以添加功能并修改驱动程序代码以使用子类——理论上应该没有什么问题,如果你需要添加数据成员,这是必要的,但是内存占用会有所不同。您的优势是在子类中同时拥有新代码和旧代码,并选择使用哪一个,基类方法或覆盖方法。

  6. 使用包含方法的类别定义修改必要的 objc 类以执行您想要的操作和/或覆盖股票类中的旧方法。

    这也可以修复库中的错误或为新硬件设备或其他自定义方法。它不是灵丹妙药,但它允许在不重新编译未更改的类/库的情况下添加类方法。原始类在代码、内存大小和入口点上是相同的,因此遗留应用程序不会中断。编译器只是将新方法作为属于该类的方式放入运行时,并使用与原始代码中相同的签名覆盖方法。

    一个例子:

    您有一个 Bing 类输出到终端,但不输出到串行端口,现在这就是您所需要的。(由于某些原因)。您的工具包中有 Bing.h 和 libBing.so,但没有 Bing.m。

    Bing 类在内部做了各种各样的事情,你甚至都不知道是什么,你只是在标题中有公共 api。

    你很聪明,所以你为 Bing 类创建了一个 (SerialOutput) 类别。

    [Bing_SerialOutput.m]
    @interface Bing (SerialOutput)   // a category
    - (void)ToSerial: (SerialPort*) port ;
    @end
    
    @implementation Bing (SerialOutput)
    - (void)ToSerial: (SerialPort*) port 
    {
    ... /// serial output code ///
    }
    @end
    

    编译器必须创建一个可以与您的应用程序链接的对象,并且运行时现在知道 Bing 响应 @selector(ToSerial:) 并且您可以像使用该方法构建 Bing 类一样使用它。您不能仅添加数据成员方法,这并不是要创建附加到基类的巨大代码肿瘤,但与严格类型的语言相比,它确实具有优势。

于 2012-05-24T05:46:24.563 回答
3

我认为其中一些答案至少表明继承是向现有类添加功能的一种较重的方式,而类别则更轻量级。

当您创建新的类层次结构(所有铃声和呼吸声)时使用继承,并且在选择作为向现有类添加功能的方法时可以说出很多工作。

正如这里的其他人所说...如果您使用继承来添加新方法,例如向 NSString,您必须去更改您在要使用此新方法的任何其他代码中使用的类型。但是,如果您使用类别,则可以简单地在现有 NSString 类型上调用该方法,而无需子类化。

两者都可以达到相同的目的,但类别似乎为我们提供了一个更简单且需要更少维护的选项(可能)。

任何人都知道是否存在绝对必要类别的情况?

于 2014-07-27T12:46:59.623 回答
2

Category 就像一个 mixin:Ruby 中的一个模块,或者有点像 Java 中的一个接口。您可以将其视为“裸方法”。添加类别时,您正在向类添加方法。维基百科的文章有好东西

于 2009-02-06T22:04:25.160 回答
0

看待这种差异的最好方法是: 1. 继承:当想要完全按照您的方式进行转换时。示例:AsyncImageView 实现延迟加载。这是通过继承 UIView 来完成的。2. category : 只是想给它增加一种额外的味道。示例:我们要替换文本字段文本中的所有空格

   @interface UITextField(setText)
      - (NSString *)replaceEscape;
   @end

   @implementation UITextField(setText)
      - (NSString *)replaceEscape
      {
         self.text=[self.text stringByTrimmingCharactersInSet:
                           [NSCharacterSet whitespaceCharacterSet]];
         return self.text;
      }
   @end

--- 它将向文本字段添加一个新属性,以便您转义所有空格。就像在不完全改变其方式的情况下为其添加一个新维度一样。

于 2012-07-06T02:31:54.210 回答