Question

There are many questions concerning the category-properties problem. I know some possibilities to address this:

From my point of view both is not clean since the memory allocated is never cleared when the object that created such properties is deallocated.

Categories are a good way to keep code clean and dynamically add functionality to already existing classes. They help to group functionality and to distributed implementation work among more developers.

The bad about categories is the missing storage.

I came across this problem several times now and I'm wondering whether the following would address this problem in an clean way that also takes care about the memory and if there are any problems that I can't see right now.

There is one restriction, that I can ignore since I'm working as a framework developer: I'm able to create my own root class that all my other classes can inherit from.

First of all declare the new root object:

@interface RootObject : NSObject

- (void)setRuntimeProperty:(id)runtimeProperty forKey:(id<NSCopying>)key;
- (id)runtimePropertyForKey:(id)key;

@end

With the corresponding implementation:

#import "RootObject.h"

@interface RootObject ()
@property (readwrite) NSMutableDictionary *runtimeProperties;
@end

@implementation RootObject
@synthesize runtimeProperties = _runtimeProperties;

- (id)init {
    self = [super init];

    if (self)
    {
        _runtimeProperties = [[NSMutableDictionary alloc] initWithCapacity:1];
    }

    return self;
}

- (void)dealloc {
    [_runtimeProperties release];
    _runtimeProperties = nil;

    [super dealloc];
}

- (id)runtimePropertyForKey:(id)key {
    return [self.runtimeProperties objectForKey:key];
}

- (void)setRuntimeProperty:(id)runtimeProperty forKey:(id<NSCopying>)key {
    if (key)
    {
        if (runtimeProperty)
        {
            [self.runtimeProperties setObject:runtimeProperty forKey:key];
        }
        else
        {
            [self.runtimeProperties removeObjectForKey:key];
        }
    }
}

@end

By using this RootObject instead of NSObject it should be very easy to add a "property" to a category on a class. Consider having some class MyClass

@interface MyClass : RootObject
// some interface here
@end

When implementing a special behavior on top of this class you are now able to add a property like this:

@interface MyClass (specialBehavior)
@property (nonatomic, retain) NSString *name;
@property (nonatomic, copy) NSDate *birthday;
@end

With corresponding implementation:

@implementation MyClass (specialBehavior)
@dynamic name;
- (NSString *)name {
    return [self runtimePropertyForKey:@"name"];
}
- (void)setName:(NSString *)name {
    [self setRuntimeProperty:name forKey:@"name"];
}
@dynamic birthday;
- (NSDate *)birthday {
    return [self runtimePropertyForKey:@"birthday"];
}
- (void)setBirthday:(NSDate *)birthday {
    [self setRuntimeProperty:[birthday copy] forKey:@"birthday"];
}
@end

Such an implementation could KVO compatible as well by just adding the necessary calls in the setter method. Very straight forward, but I'm wondering whether I missed something important? (E.g. very very bad runtime performance having many such declared properties or using many of these objects)

Was it helpful?

Solution

This is effectively the same as objc_setAssociatedObject and objc_getAssociatedObject, which do release memory when the object is deallocated (depending on the association type). I would guess they also have much lower overhead than your suggested code.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top