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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/522341/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-03 21:07:30  来源:igfitidea点击:

What is the difference between inheritance and Categories in Objective-C

objective-coopinheritancecategories

提问by hhafez

Can some one explain to me the difference between categories and inheritance in Objective C? I've read the entry in Wikipediaand the discussion on categories there doesn't look any different to that of inheritance. I also looked at the discussion on the topic in the book "Open iPhone Development" and I still don't get it.

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

回答by Abizern

Sometimes, inheritance just seems like more trouble than it is worth. It is correctly used when you want to add something to an existing class that is a change in the behaviour of that class.

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

With a Category, you just want the existing object to do a little more. As already given, if you just want to have a string class that handles compression, you don't need to subclass the string class, you just create a category that handles the compression. That way, you don't need to change the type of the string classes that you already use.

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

The clue is in the restriction that categories only add methods, you can't add variables to a class using categories. If the class needs more properties, then it has to be subclassed.(edit: you can use associative storage, I believe).

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

Categories are a nice way to add functionality while at the same time conforming to an object oriented principle to prefer composition over inheritance.

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

Edit January 2012

2012 年 1 月编辑

Things have changed now. With the current LLVM compiler, and the modern, 64-bit runtime, you can add iVars and properties to class extensions(not categories). This lets you keep private iVars out of the public interface. But, if you declare properties for the iVars, they can still be accessed / changed via KVC, because there is still no such thing as a private method in Objective-C.

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

回答by Terry Wilcox

Categories allow you to add methods to existing classes. So rather than subclass NSData to add your funky new encryption methods, you can add them directly to the NSData class. Every NSData object in your app now has access to those methods.

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

To see how useful this can be, look at: CocoaDev

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

回答by amrox

One of favorite illustrations of Objective-c categories in action is NSString. NSString is defined in the Foundation framework, which has no notion of views or windows. However, if you use an NSString in a Cocoa application you'll notice it responds to messages like – drawInRect:withAttributes:.

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

AppKit defines a category for NSString that provides additional drawing methods. The category allows new methods to be added to an existing class, so we're still just dealing with NSStrings. If AppKit instead implemented drawing by subclassing we'd have to deal with 'AppKitStrings' or 'NSSDrawableStrings' or something like that.

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

Categories let you add application or domain specific methods to existing classes. It can be quite powerful and convenient.

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

回答by Chris Reid

If you as a programmer are given a complete set of source code for a code library or application, you can go nuts and change whatever you need to achieve your programming goal with that code.

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

Unfortunately, this is not always the case or even desirable. A lot of times you are given a binary library/object kit and a set of headers to make do with.

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

Then a new functionality is needed for a class so you could do a couple of things:

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

  1. create a new class whole instead of a stock class -- replicating all its functions and members then rewrite all the code to use the new class.

  2. create a new wrapper class that contains the stock class as a member (compositing) and rewrite the codebase to utilize the new class.

  3. binary patches of the library to change the code (good luck)

  4. force the compiler to see your new class as the old one and hope it does not depend on a certain size or place in memory and specific entry points.

  5. subclass specialization -- create subclasses to add functionality and modify driver code to use the subclass instead -- theoretically there should be few problems and if you need to add data members it is necessary, but the memory footprint will be different. You have the advantage of having both the new code and the old code available in the subclass and choosing which to use, the base class method or the overridden method.

  6. modify the necessary objc class with a category definition containing methods to do what you want and/or override the old methods in the stock classes.

    This can also fix errors in the library or customize methods for new hardware devices or whatever. It is not a panacea, but it allows for class method adding without recompiling the class/library that is unchanged. The original class is the same in code, memory size, and entry points, so legacy apps don't break. The compiler simply puts the new method(s) into the runtime as belonging to that class, and overrides methods with the same signature as in the original code.

    an example:

    You have a class Bing that outputs to a terminal, but not to a serial port, and now that is what you need. (for some reason). You have Bing.h and libBing.so, but not Bing.m in your kit.

    The Bing class does all kinds of stuff internally, you don't even know all what, you just have the public api in the header.

    You are smart, so you create a (SerialOutput) category for the Bing class.

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

    The compiler obliges to create an object that can be linked in with your app and the runtime now knows that Bing responds to @selector(ToSerial:) and you can use it as if the Bing class was built with that method. You cannot add data members only methods and this was not intended to create giant tumors of code attached to base classes but it does have its advantages over strictly typed languages.

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

回答by user3881658

I think some of these answers at least point to the idea that inheritance is a heavier way of adding functionality to an existing class, while categories are more lightweight.

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

Inheritance is used when you're creating a new class hierarchy (all the bells and whistles) and arguably brings alot of work when chosen as the method of adding functionality to existing classes.

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

As someone else here put it... If you are using inheritance to add a new method for example to NSString, you have to go and change the type you're using in any other code where you want to use this new method. If, however, you use categories, you can simply call the method on existing NSString types, without subclassing.

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

The same ends can be achieved with either, but categories seem to give us an option that is simpler and requires less maintenance (probably).

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

Anyone know if there are situations where categories are absolutely necessary?

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

回答by Charlie Martin

A Category is like a mixin: a module in Ruby, or somewhat like an interface in Java. You can think of it as "naked methods". When you add a Category, you're adding methods to the class. The Wikipedia article has good stuff.

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

回答by Avik Roy

The best way to look at this difference is that: 1. inheritance : when want to turn it exactly in your way. example : AsyncImageView to implement lazy loading. Which is done by inheriting UIView. 2. category : Just want to add a extra flavor to it. example : We want to replace all spaces from a textfield's text

看待这种差异的最佳方式是: 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

--- It will add a new property to textfield for you to escape all white spaces. Just like adding a new dimension to it without completely changing its way.

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