57

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.

nevan king
  • 112,709
  • 45
  • 203
  • 241
hhafez
  • 38,949
  • 39
  • 113
  • 143

7 Answers7

96

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

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.

Jack Humphries
  • 13,056
  • 14
  • 84
  • 125
Abizern
  • 146,289
  • 39
  • 203
  • 257
  • 3
    It should be noted, you *can* declare properties for existing ivars with Categories. By that I mean, you can declare an `@property` and either `@synthesize` or `@dynamic` them in the implementation. I have done this with `readonly` properties, although I imagine they could be used for anything. That is, assuming `MyClass` has `int myVar`, then `MyClass (MyCategory)` can have a property for `myVar`. – jbrennan Nov 04 '09 at 19:45
  • 5
    True, but that still doesn't _add_ storage to the class. – Abizern Nov 04 '09 at 19:50
  • There is such a thing as a private method in Objective-C. It's a method that's defined in the @implementation or declared in a nameless category. – Cthutu Apr 26 '12 at 20:02
  • 2
    @Cthutu that's private by convention. If you try to call such a method you will get a compiler warning, but Objective-C is dynamic, and you can still send the message to the class even if it is not publicly declared. That's what I mean when I say there are no private classes. You can still send the message to the class. – Abizern Apr 27 '12 at 00:14
  • You can add properties to categories (http://stackoverflow.com/a/8733320/1226304) and this might be useful, when you're creating category for class that doesn't support subclassing, e.g. `UIAlertView`. – derpoliuk Nov 16 '13 at 17:07
  • @StasDerpoliuk You could always add associative storage to classes using the runtime, but that isn't the same as a property. – Abizern Nov 16 '13 at 19:01
  • @Abizern I see. But the point is that in some cases it might be useful. – derpoliuk Nov 16 '13 at 21:07
  • What is also to add here, is when you think of the extendability of it. You can inherit from an inherited class (plant <- tree <- oak <- Canadian oak). But you can't do that with categories.To me besides the properties, this is the main difference. – lukas_o Jun 22 '15 at 12:31
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.

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

Terry Wilcox
  • 9,010
  • 1
  • 34
  • 36
  • are you sure that just by making a category out of NSData everywhere NSData is used the category is used, I thought you need to make the category pose as NSData for it to be used instead of NSData – hhafez Feb 07 '09 at 00:58
  • 3
    It doesn't automatically apply to NSData objects, you still need to #import the header where you define the category. – Abizern Feb 07 '09 at 01:38
  • No, classes pose. All NSData objects get the methods of categories that are loaded. – Chuck Feb 07 '09 at 02:23
  • And it doesn't matter if you import the header — the objects still get the method. The compiler will complain if you try to call it directly, but you can still call it with performSelector:. – Chuck Feb 07 '09 at 02:32
  • 1
    A category is not a class; it doesn't replace the existing class. It adds methods to the existing class. – Terry Wilcox Feb 09 '09 at 14:55
  • 1
    The link to CocoaDev is down – E-Riddie Dec 04 '14 at 14:14
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:.

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.

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

amrox
  • 6,207
  • 3
  • 36
  • 57
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.

  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.

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
Chris Reid
  • 460
  • 4
  • 9
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.

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?

serge-k
  • 3,394
  • 2
  • 24
  • 55
  • Sounds like you should start a separate question regarding "...there are situations where categories are absolutely necessary?" – serge-k Apr 06 '16 at 04:00
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.

mahboudz
  • 39,196
  • 16
  • 97
  • 124
Charlie Martin
  • 110,348
  • 25
  • 193
  • 263
  • but you are still making a new class aren't you? So what is the difference between that and inheritance – hhafez Feb 07 '09 at 00:59
  • 1
    No, you are not making a new class. You are adding methods to an existing class. – Chuck Feb 07 '09 at 02:35
  • @charlie-martin - There is major difference between Ruby Mixins and Objective-C categories, a Ruby Module is not related to a Class it is being mixed in, but a Category is "attached" to the Class. – rnk Sep 07 '13 at 00:00
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

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

Avik Roy
  • 187
  • 1
  • 3
  • 3
    I think this is a very confusing answer. The grammar is awful. It is totally unclear what is meant by poetic phenomenolgy such as 'flavor', 'dimension', and 'the way'. The naming of a category (setText), and the method (replaceEscape), to remove spaces from a textfield are badly chosen and not descriptive. Also, it is plain wrong to say that categories add new properties to the textfield. I am asuming that 'property' in this context should be interpretted as 'method'. – Fnord23 Mar 28 '14 at 10:08
  • Its just adding a method. `replaceEscape` to the class IUTextfield. – Chris Reid May 26 '17 at 08:17