6

I have a category on an existing class that adds a property and a few methods to the class.

@interface AClass (ACategory) {
    NSString *aProperty;
}

@property (nonatomic, retain) NSString *aProperty;

@end

In the implementation file, I want to release this property when the object is deallocated. However, if I declare dealloc in this class, it will override the dealloc from the original class from what I understand. What then is the proper way to release this aProperty when the object is deallocated?

@implementation AClass (ACategory)

@synthesize aProperty;

- (void)dealloc {
    [aProperty release];
    // this will skip the original dealloc method from what I understand
    [super dealloc];
}

@end
Anurag
  • 132,806
  • 34
  • 214
  • 257

2 Answers2

12

Well, this is a little problematic, since your code is wrong.

  1. You can't declare instance variables in a category; using the latest Objective-C ABI, you can declare new instance variables within a class extension (@interface AClass () {//...), but that is different from a category (@interface AClass (ACategory)).
  2. Even if you could, the syntax for instance variable declaration is that they be enclosed in curly braces after the @interface line.

You can declare a property in a category, but you'll have to define its storage without using a new instance variable (hence, @dynamic instead of @synthesize).


As to your actual question, you can't call the original implementation of an overridden method unless you use method-swizzling (facilitated by runtime functions like method_exchangeImplementations). I recommend against doing this anyway; it's really frightening and dangerous.


Update: Explanation of Instance Variables in Class Extensions

A class extension is like a category, but it is anonymous and must be placed within the .m file associated with the original class. It looks like:

@interface SomeClass () {
    // any extra instance variables you wish to add
}
@property (nonatomic, copy) NSString *aProperty;
@end

Its implementation must be in the main @implementation block for your class. Thus:

@implementation SomeClass
// synthesize any properties from the original interface
@synthesize aProperty;
// this will synthesize an instance variable and accessors for aProperty,
// which was declared in the class extension.
- (void)dealloc {
    [aProperty release];
    // perform other memory management
    [super dealloc];
}
@end

So, a class extension is useful for keeping private instance variables and methods out of the public interface, but will not help you add instance variables to a class over which you haven't control. There is no issue with overriding -dealloc, because you just implement it like you normally would, whilst including any necessary memory management for the instance variables you introduced within the class extension.

Please note that this stuff works only with the latest 64-bit Objective-C ABI.

Jonathan Sterling
  • 18,003
  • 12
  • 64
  • 79
  • 1
    +1 for "method swizzling ... really frightening and dangerous" – BoltClock Dec 06 '10 at 20:20
  • 1
    thanks @Jonathan.. I mocked up the example in the SO editor, but thanks for pointing out the syntax error. – Anurag Dec 06 '10 at 20:53
  • 1
    If this was helpful, it would be great if you could accept this as the as the answer to your question. Otherwise, let me know if there's still anything confusing you, and I'll do my best to clarify it. :) – Jonathan Sterling Dec 07 '10 at 06:42
  • 1
    could you elaborate more on how declaring an instance variable will work with a class extension? how would we take care of the dealloc override problem? thanks :) – Anurag Dec 07 '10 at 17:49
5

As an aside, you can use associated references to "simulate the addition of object instance variables to an existing class".

Essentially, you can add an associated object as below:

static void* ASI_HTTP_REQUEST;  // declare inside the category @implementation but outside any method    

// And within a method, init perhaps
objc_setAssociatedObject(self, 
    &ASI_HTTP_REQUEST, 
    request, 
    OBJC_ASSOCIATION_RETAIN);

And release the associated object by sending 'nil':

// And release the associated object
objc_setAssociatedObject(self,
    &ASI_HTTP_REQUEST, 
    nil, 
    OBJC_ASSOCIATION_RETAIN);

The Apple documentation is here.

It took me a while to find, so I hope that it helps someone.

paulkmoore
  • 3,053
  • 2
  • 19
  • 20
  • 1
    Good! But one last question, when would you nil-out your associated object? How do you know that the category is going to be released? – David Hernandez Aug 08 '13 at 17:00