Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Giving each subclass its own copy of a class variable

I have the following class in my iOS application (it is like an abstract class from the Java world).

@implementation WSObject

static NSDictionary* _dictionary = nil;
+(NSDictionary*) dictionary {
    if (_dictionary == nil) {
        _dictionary = [NSKeyedUnarchiver unarchiveObjectWithFile:[self localStorePath]];
    }
    return _dictionary;
}

...

@end

I then have multiple classes which implement this above WSObject with the class method dictionary. The problem is, that each of these classes should have their own _dictionary, but they are all sharing the same object from the super class. I could, of course, copy to all the subclasses, but that would break the reusability. Besides this getter, there are other class methods in WSObject which mutate the dictionary. Because of this, there would be a several class methods which should be in every subclass.

How can I solve this in a smart way? Please tell me if my description is insufficient.

like image 571
dhrm Avatar asked Dec 02 '22 00:12

dhrm


1 Answers

Associative references seem like they'll do the trick. You can essentially tack some storage on to the class object itself. (I'm using NSStrings here, in place of the dictionaries you want to use, just for demonstration.)

Superclass:

#import <Foundation/Foundation.h>
#import <objc/runtime.h>

@interface Stuper : NSObject

// Accessor method for the "class variable"
+ (NSString *) str;
// Analog to your +localStorePath
+ (NSString *) quote;

@end

#import "Stuper.h"

// The doc suggests simply using the address of a static variable as the key.
// This works fine, even though every class is (as in your problem) using
// the same key, because we are associating to a different class each time.
static char key;    
@implementation Stuper

+ (NSString *) str {
    NSString * s = objc_getAssociatedObject(self, &key);
    if( !s ){
        s = [self quote];
        // You'll probably want to use OBJC_ASSOCIATION_RETAIN for your dictionary.
        // self inside a class method is the class object; use that as
        // the associator. The string is now tied to the associator, i.e.,
        // has the same lifetime.
        objc_setAssociatedObject(self, &key, s, OBJC_ASSOCIATION_COPY);
    }
    return s;
}

+ (NSString *) quote {
    return @"It was the best of times, it was the worst of times.";
}

@end



Subclass:

#import "Stuper.h"
@interface Stub : Stuper @end

#import "Stub.h"

@implementation Stub

+ (NSString *) quote {
    return @"Call me Ishmael.";
}

@end



Trying this out:

#import <Foundation/Foundation.h>
#import "Stuper.h"
#import "Stub.h"

int main (int argc, const char * argv[])
{

    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

    NSLog(@"%@", [Stuper str]);
    NSLog(@"%@", [Stub str]);

    [pool drain];
    return 0;
}

Each class object now has its own string, associated with it.

2011-12-05 23:11:09.031 SubClassVariables[36254:903] It was the best of times, it was the worst of times.
2011-12-05 23:11:09.034 SubClassVariables[36254:903] Call me Ishmael.

The only downside here is that you'll have to call the accessor method every time you want the object; you don't have a pointer you can use directly. You can call objc_getAssociatedObject in the superclass as an accessor, too, of course, since it has access to key.

like image 175
jscs Avatar answered Jan 14 '23 15:01

jscs