• Open Menu Close Menu
  • Apple
  • Shopping Bag
  • Apple
  • Mac
  • iPad
  • iPhone
  • Watch
  • TV
  • Music
  • Support
  • Search apple.com
  • Shopping Bag

Lists

Open Menu Close Menu
  • Terms and Conditions
  • Lists hosted on this site
  • Email the Postmaster
  • Tips for posting to public mailing lists
Re: Categories vs. subclassing
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Categories vs. subclassing


  • Subject: Re: Categories vs. subclassing
  • From: "Erik M. Buck" <email@hidden>
  • Date: Tue, 26 Oct 2004 13:58:24 -0400

I will assume that readers are already familiar with Apple's Objective-C manual that describes categories and add some more references and excerpts from books.


http://www.stepwise.com/Articles/Technical/CategoricallySpeaking.html
http://www.stepwise.com/Articles/Technical/PosersAndCategories/
http://www.stone.com/The_Cocoa_Files/Categorically_Speaking.html
http://www.toodarkpark.org/computers/objc/ "OBJECT-ORIENTED PROGRAMMING AND THE OBJECTIVE-C LANGUAGE"
http://www.cocoabuilder.com/search/archive/cocoa?words=category+subclass
http://www.gnustep.org/resources/ObjCFun.html
http://docs.sun.com/db/doc/802-2110/6i63kq4u0?a=view
http://www.google.com/search? num=100&hl=en&lr=&as_qdr=all&q=category+subclass+objective- c&btnG=Search

http://www.cocoaprogramming.net/Downloads.html includes the examples from "Cocoa Programming" including Appendix A which among other things shows how to call the original method implementation from a method override in a category. "Cocoa Programming" Chapter 26 shows some nice design time uses for categories and provides guidelines for sensible usage. There are countless examples of category use in "Cocoa Programming".

From "Cocoa Programming" Chapter 4
When categories were first introduced, NeXT recommended that they be used to break large implementation files into several smaller files so they could be used to organize the methods. For example, all the private methods that should not be called except by the class’s author can be organized into a category that is concealed from other programmers. There is no way to restrict which methods of a class can be called in which contexts, but methods can be hidden from the users of a class. The extra effort to find out which hidden methods exist is usually enough to discourage their use.

Categories containing private methods are often added within implementation files so that there is no header file that declares the methods, and they can still be used within the object’s own implementation without warnings. In fact, category declarations do not need an interface at all. Only the implementation is necessary.

Categories are useful for organizational purposes, but that barely touches the power and flexibility enabled by them. Methods can be added to any class without needing the source code for the class that is extended, or recompiling. Categories are an alternative to subclassing with some limitations. One limitation is that categories cannot be used to add instance variables to a class the way a subclass can. [However, it is possible to simulate adding instance variables] Nevertheless, using categories is preferable to subclassing in many situations. For example, suppose your application calls a method implemented by a Cocoa framework class to obtain an object. The class of the object returned by the framework was determined when the framework was written. Subclassing the returned object won’t help because there might not be a way to get the framework to return your subclass instead of the class that was compiled into the framework. The class returned by the framework can be extended by a category implemented in your code to add the methods you need.

Methods added by a category can override existing implementations, and it is possible to patch bugs in classes to which you have no source code. To do so, replace the offending method with a correct implementation in a category. A restriction when replacing methods is that there is no convenient way to call the original implementation from the overriding implementation. [However, Appendix A shows one way to do it] Also, if more than one category implements the same method, then it is unpredicitable which method will be chosen for use by the runtime.

Methods that are implemented in a category can access all the extended class’ instance variables directly. At runtime, methods that are declared in a category are no different from methods declared in the class interface. All subclasses of the extended class also gain the category’s methods. Even preexisting instances gain the category’s methods when code containing a category is dynamically loaded during a program’s execution. It is possible to have an object that does not understand certain messages when the application starts, but does understand them after a plug-in containing a category has been loaded.

Categories are a powerful feature that can be easily abused. A good practice is to add a unique prefix to the start of any method names defined in categories that modify framework classes. The prefix reduces the chance of an accidental clash with a hidden framework method or a method in another category, which can happen easily. After a while, programmers get in the habit of naming methods according to the conventions used in Cocoa. If you think of a method to add to a Cocoa class, there is a good chance someone else has thought of the same method and given it the same name. Another danger is that Apple will add the same method in a future release, but the method will be masked by a preexisting category. Even if a method added via a category does not create a conflict now, it may in a future version of Cocoa.



The as yet unreleased "Cocoa Design Patterns" includes a chapter on associative storage that provides this example of a way to simulate adding instance variables via a category and accessor methods. Here is an excerpt from Chapter 9:

Simulating Instance Variables

One limitation of the Category pattern described in Chapter 8, "Category", is that categories can only add methods to a class; instance variables must be declared only in the main class interface. This example shows one way the Associative Storage pattern is used to simulate the addition of an instance variable to Cocoa’s NSObject class. The category in this example provides access to a different label for each instance of NSObject or any class that inherits from NSObject. Instances that don't have an assigned label don't consume any extra memory. The following category declares the –mySetLabel: and –myLabel methods:

#import <Foundation/Foundation.h>


@interface NSObject (MYSimulateIVar)

- (void)mySetLabel:(NSString *)aString;
- (NSString *)myLabel;

@end

Methods like the ones defined in this category are called Accessors. Accessors are themselves an important pattern described in Chapter 23. The primary purpose of Accessors is to funnel all references to each instance variable through a few – usually only two - methods. A nice benefit of using accessors in this example is that even though the labels are not stored as instance variables, programmers using the NSObject class doesn't need to know that. The accessors shield users of a class from the actual implementation.
#import "MYSimulateIvar.h"

@implementation NSObject (MYSimulateIVar)

//
static NSMapTable *_MYSimulatedIVarMapTable = NULL;


+ (NSMapTable *)_mySimulatedIVarMapTable
//
{
if(NULL == _MYSimulatedIVarMapTable)
{
_MYSimulatedIVarMapTable = NSCreateMapTable(
NSNonRetainedObjectMapKeyCallBacks, NSObjectMapValueCallBacks, 16);
}

return _MYSimulatedIVarMapTable;
}


- (void)dealloc
//
{
NSMapRemove([[self class] _mySimulatedIVarMapTable], self);
}


- (void)mySetLabel:(NSString *)aString
//
{
NSString *newLabel = [aString copy];

NSMapInsert([[self class] _mySimulatedIVarMapTable], self, newLabel);
[newLabel release];
}


- (NSString *)myLabel
//
{
return NSMapGet([[self class] _mySimulatedIVarMapTable], self);
}

@end

There are several important elements to the implementation of the MYSimulateIVar category. The +_myRefCountMapTable class method is used to access the NSMapTable data structure that stores labels associated with NSObject instances. The +_myRefCountMapTable method is not declared in the category interface because it is a private implementation detail of the category. The first time +_myRefCountMapTable is called, the data structure is initialized to store non-retained object keys and retained objects as values. It is critical that the keys are not retained because if they are retained it will be impossible to correctly deallocate any instances of NSObject that have associated labels. The table is initialized with sufficient storage for 16 key/value pairs, but that number is arbitrary. The storage for the table automatically increases as keys and values are added. The –dealloc method implemented in the category replaces NSObject’s existing implementation. The –dealloc method removes any key/value pair associated with an instance when the instance is deallocated. It is safe to replace NSObject’s -dealloc implementation in this case because the replaced version is documented to do nothing at this time. If Apple ever changes the implementation of –dealloc in the NSObject class, the fact that this category bypasses that implementation my have undesirable side effects.
To flesh out support for labels associated with objects, it’s necessary to provide encoding and decoding support so that labels are stored along with any other data stored for objects when they are encoded. An example of using existing accessors in the implementation of encoding and decoding methods is provided in Chapter 13, “Archiving and Unarchiving.” Support for copying labels when objects are copied should also be supported, and a general technique is described in Chapter 14, “Copying.”

Finally, this example is limited to storing of labels for objects. A more useful category enables the storage on any amount of data with each object. To enable that, modify the example to store dictionaries of key/value pairs with –mySetUserInfo: and –myUserInfo methods instead of instead of -mySetLabel: and –myLabel methods.

- (void)mySetUserInfo:(NSDictionary *)aDictionary
//
{
NSDictionary *newDictionary = [aDictionary copy];

NSMapInsert([[self class] _mySimulatedIVarMapTable], self, newDictionary);
[newDictionary release];
}


- (NSString *)myUserInfo
//
{
return NSMapGet([[self class] _mySimulatedIVarMapTable], self);
}

Any number of key/value pairs can be stored in the dictionary associated with each object. To keep the ability to store labels, simply store a label string associated with a key such as @”Label” in each user info dictionary.
Cocoa’s NSNotification class uses user info dictionaries to pass arbitrary data along with each NSNotification instance.



_______________________________________________
Do not post admin requests to the list. They will be ignored.
Cocoa-dev mailing list (email@hidden)
Help/Unsubscribe/Update your Subscription:

This email sent to email@hidden
  • Prev by Date: Re: NSBox Colored Background
  • Next by Date: System Idle Time
  • Previous by thread: Re: Categories vs. subclassing
  • Next by thread: Copy/Pasting to MS Word
  • Index(es):
    • Date
    • Thread