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

时间:2022-09-09 22:29:17

Can some one explain to me the difference between categories and inheritance in Objective C? I've read the entry in Wikipedia and 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中的类别和继承的区别吗?我读过*的条目,关于类别的讨论与继承没有什么不同。我还看了《开放iPhone开发》一书中关于这个话题的讨论,但我还是不明白。

7 个解决方案

#1


95  

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.

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

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位运行时,您可以向类扩展(而不是类别)添加iVars和属性。这使您可以在公共接口之外保留私有iVars。但是,如果您为iVars声明属性,它们仍然可以通过KVC访问/更改,因为Objective-C中仍然没有私有方法。

#2


17  

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.

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

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

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

#3


11  

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定义了一个类别,该类别提供了其他绘图方法。category允许将新方法添加到现有类中,因此我们仍然只处理nsstring。如果AppKit通过子类化来实现绘图,我们就必须处理“AppKitStrings”或“NSSDrawableStrings”之类的东西。

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

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

#4


4  

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.

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

  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.

    使用包含方法的类别定义修改必要的objc类,以完成所需的工作,并/或覆盖stock类中的旧方法。

    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.

    你有一个把输出到终端的类,而不是一个串行端口,现在这就是你需要的。(由于某种原因)。你必应。h和拼凑。所以,但不是必应。在你的装备。

    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.

    Bing类在内部做各种事情,你甚至不知道什么,你只需要在header中有公共api。

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

    您很聪明,所以您为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
    

    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.

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

#5


3  

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?

有人知道在某些情况下类别是绝对必要的吗?

#6


2  

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.

一个类别就像一个mixin:一个Ruby中的模块,或者有点像Java中的一个接口。你可以把它想象成“赤裸裸的方法”。当您添加一个类别时,您正在为类添加方法。*的文章有很多好东西。

#7


0  

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。品类:只是想给它增加一点味道。示例:我们想要从textfield的文本中替换所有空格。

   @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.

---它将向textfield添加一个新属性,使您可以转义所有空格。就像在不完全改变方向的情况下添加一个新的维度。

#1


95  

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.

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

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位运行时,您可以向类扩展(而不是类别)添加iVars和属性。这使您可以在公共接口之外保留私有iVars。但是,如果您为iVars声明属性,它们仍然可以通过KVC访问/更改,因为Objective-C中仍然没有私有方法。

#2


17  

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.

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

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

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

#3


11  

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定义了一个类别,该类别提供了其他绘图方法。category允许将新方法添加到现有类中,因此我们仍然只处理nsstring。如果AppKit通过子类化来实现绘图,我们就必须处理“AppKitStrings”或“NSSDrawableStrings”之类的东西。

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

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

#4


4  

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.

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

  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.

    使用包含方法的类别定义修改必要的objc类,以完成所需的工作,并/或覆盖stock类中的旧方法。

    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.

    你有一个把输出到终端的类,而不是一个串行端口,现在这就是你需要的。(由于某种原因)。你必应。h和拼凑。所以,但不是必应。在你的装备。

    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.

    Bing类在内部做各种事情,你甚至不知道什么,你只需要在header中有公共api。

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

    您很聪明,所以您为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
    

    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.

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

#5


3  

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?

有人知道在某些情况下类别是绝对必要的吗?

#6


2  

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.

一个类别就像一个mixin:一个Ruby中的模块,或者有点像Java中的一个接口。你可以把它想象成“赤裸裸的方法”。当您添加一个类别时,您正在为类添加方法。*的文章有很多好东西。

#7


0  

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。品类:只是想给它增加一点味道。示例:我们想要从textfield的文本中替换所有空格。

   @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.

---它将向textfield添加一个新属性,使您可以转义所有空格。就像在不完全改变方向的情况下添加一个新的维度。