Objective-C中的继承和类别有什么区别

时间:2009-02-06 21:53:16

标签: objective-c oop inheritance categories

有人可以向我解释Objective C中类别和继承之间的区别吗?我读过the entry in Wikipedia,关于那里的类别的讨论与继承没有任何不同。我还在“打开iPhone开发”一书中查看了有关该主题的讨论,但我仍然没有得到它。

7 个答案:

答案 0 :(得分:96)

有时,继承似乎比它的价值更麻烦。当您想要向现有类添加某些内容时,可以正确使用它,这是对该类行为的更改。

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

线索是限制类别只添加方法,您不能使用类别向类添加变量。如果类需要更多属性,那么它必须是子类。(编辑:你可以使用关联存储,我相信)。

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

修改2012年1月

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

答案 1 :(得分:17)

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

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

答案 2 :(得分:11)

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

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

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

答案 3 :(得分: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类是使用该方法构建的一样。您不能仅添加数据成员方法,这不是为了创建附加到基类的巨大代码肿瘤,但它确实比严格类型语言更有优势。

答案 4 :(得分:3)

我认为其中一些答案至少指出了继承是向现有类添加功能的更重要方式的想法,而类别更轻量级。

当你创建一个新的类层次结构(所有的花里胡哨)时,会使用继承,并且当被选择作为向现有类添加功能的方法时,可以带来很多工作。

正如其他人在这里说的那样......如果你使用继承来为NSString添加一个新的方法,你必须改变你在任何其他想要使用这个新代码的代码中使用的类型方法。但是,如果使用类别,则只需在现有NSString类型上调用该方法,而无需进行子类化。

使用其中任何一个都可以实现相同的目标,但类别似乎为我们提供了一个更简单且需要更少维护(可能)的选项。

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

答案 5 :(得分:2)

类似于mixin:Ruby中的模块,或者有点像Java中的接口。你可以把它想象成“裸体方法”。添加类别时,您将向该类添加方法。维基百科的文章有good stuff

答案 6 :(得分:0)

了解这种差异的最佳方法是: 1.继承:什么时候想要以你的方式完全转向它。 示例:AsyncImageView实现延迟加载。这是通过继承UIView来完成的。 2.类别:只是想为它添加额外的味道。 示例:我们想要替换文本字段文本中的所有空格

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

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

---它将为textfield添加一个新属性,以便您逃脱所有空白区域。就像在不完全改变方式的情况下为其添加新维度一样。