Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Objective-C - dynamic singleton initializer?

I have a base Store class with many methods that all stores inherit. Each store is a singleton. Right now, each store defines its own near-identical method:

+ (Store *)instance {
    static SubStore *store = nil;
    if (!store) {
        store = (SubStore *) [[super allocWithZone:nil] init];
        [store setupDefaults];
    }
    return store;
}

Is there any way to craft a singleton method in a way that the method can simply be added to the base class and inherited by the subclasses?

like image 802
Stefan Kendall Avatar asked May 16 '13 02:05

Stefan Kendall


2 Answers

Stick with simple/stupid, but use dispatch_once.

As soon as you try to make it generic, it gets complex. And buggy.

The sharedInstance method with explicit class naming is dead obvious and it is unlikely you'll repeat it more than a few times in your project.

If, however, you have many subclasses of the one class, then move to an identifier model. I.e. a cache on the abstract store class that can look up store instances by identifier.


I avoid using either +initialize or, worse, +load for such initialization. Both are non-deterministic in that their order of execution in relation to other subsystems in your app could be quite variable with seemingly innocuous changes.

Far better to have entirely deterministic initialization. Add a callout in your applicationDidFinishLaunching: (or one of the others) that explicitly initializes this particular subsystem in your app. It is easy to follow, explicit in both declaration and use, and won't change behavior in odd ways as your codebase evolves.

like image 132
bbum Avatar answered Oct 14 '22 03:10

bbum


Rather than using one store static SubStore * store = nil;, you could use a NSMutableDictionary which uses class names as keys.

Briefly:

#import <Foundation/Foundation.h>

@interface MONStore : NSObject
- (NSString *)nameOfMostPopularItem;
@end

@implementation MONStore

+ (instancetype)sharedStore
{
    // lazy population - not thread safe
    static NSMutableDictionary * stores = nil;
    if (nil == stores) {
        stores = [NSMutableDictionary new];
    }
    NSString * key = NSStringFromClass([self class]);
    if (nil == [stores objectForKey:key]) {
        [stores setObject:[self new] forKey:key];
    }
    return [stores objectForKey:key];
}

- (NSString *)nameOfMostPopularItem
{
    return nil;
}

@end

@interface MONMusicStore : MONStore
@end

@implementation MONMusicStore
- (NSString *)nameOfMostPopularItem { return @"Guitar Strings"; }
@end

@interface MONPetStore : MONStore
@end

@implementation MONPetStore
- (NSString *)nameOfMostPopularItem { return @"Puppies"; }
@end

int main(int argc, const char * argv[])
{
    @autoreleasepool {
        NSLog(@"--- Shopping List ---\nMusic Store:\n\t%@\n\nPet Store:\n\t%@\n",
                    [MONMusicStore sharedStore].nameOfMostPopularItem,
                    [MONPetStore sharedStore].nameOfMostPopularItem
                    );
    }
    return 0;
}

...not that I would ever do this in my program.

like image 35
justin Avatar answered Oct 14 '22 03:10

justin