14

My question is the following: I have a singleton type object (I'm using ARC) that has this code in the implementation file

+(id)sharedInstance 
{
    static DataManager *sharedInstance;
    if (sharedInstance == nil) {
        sharedInstance = [[DataManager alloc] init];
    }
    return sharedInstance;
}

+(NSManagedObjectContext *)getManagedContext
{
    AppDelegate *applicationDelegate =(AppDelegate *)[[UIApplication sharedApplication] delegate];
    return [applicationDelegate managedObjectContext];
}

+(void)saveContext:(NSManagedObjectContext *)context
{
    NSError *error;
    if (![context save:&error]) {
        NSLog(@"Whoops, couldn't save: %@", [error localizedDescription]);
    }
}

#pragma mark - Data management methods

+(void)addPersonWithName:(NSString *)name andPicture:(UIImage *)picture
{
    NSManagedObjectContext *context = [self getManagedContext]; //no problem here
    //some code 
    [self saveContex:context]; // no known class method for selector saveContext:
}

Why is that? The method is declared in the .h file with + ... the getManagedContext model doesn't give this error????

Borys Verebskyi
  • 4,160
  • 6
  • 28
  • 42
user1028028
  • 6,323
  • 9
  • 34
  • 59
  • Please show the .h file as well with the method declarations – jrturton Nov 03 '11 at 15:37
  • 1
    This is not a singleton issue - CocoaFu had it right (but has deleted the answer): your selector is missing a 't'. it is used as `[self saveContex:`. – justin Nov 03 '11 at 15:50

1 Answers1

61

The keyword self inside a method references the owner of the method, which is the instance of the object for instance methods, and the class for class methods. However, the message saveContex is missing a t at the end (saveContext).

dispatch_once singleton

And here is a better singleton idiom compatible with ARC:

+(MySingleton *)sharedInstance {
    static dispatch_once_t pred;
    static MySingleton *shared = nil;
    dispatch_once(&pred, ^{
        shared = [[MySingleton alloc] init];
    });
    return shared;
}

Same code as Xcode template

Same code as a Xcode template with placeholders:

+ (<#class#> *)shared<#name#> {
    static dispatch_once_t onceToken;
    static <#class#> *shared<#name#> = nil;
    dispatch_once(&onceToken, ^{
        shared<#name#> = <#initializer#>;
    });
    return shared<#name#>;
}

Same code + disabled alloc/init/new

Want to clue users that they should call sharedInstance instead alloc/init/new? You can disable methods with attribute unavailable. This will cause a compiler error if any of those methods is called on the class.

#import <Foundation/Foundation.h>

@interface MySingleton : NSObject

+(instancetype) sharedInstance;

// clue for improper use (produces compile time error)
+(instancetype) alloc __attribute__((unavailable("alloc not available, call sharedInstance instead")));
-(instancetype) init __attribute__((unavailable("init not available, call sharedInstance instead")));
+(instancetype) new __attribute__((unavailable("new not available, call sharedInstance instead")));

@end

#import "MySingleton.h"

@implementation MySingleton

+(instancetype) sharedInstance {
    static dispatch_once_t pred;
    static id shared = nil;
    dispatch_once(&pred, ^{
        shared = [[super alloc] initUniqueInstance];
    });
    return shared;
}

-(instancetype) initUniqueInstance {
    return [super init];
}

@end

Warning: dispatch_once is not reentrant

Don't make a recursive call to sharedInstance from inside the dispatch_once block.

If you call dispatch_once from several threads it will act as a barrier preventing concurrent access. But if you call it again in the same thread from inside the block it will deadlock the thread. This example illustrates the problem:

#import <Foundation/Foundation.h>

static NSRecursiveLock *_lock = nil;
// constructor = run before main. used = emit code even if the function is not referenced.
// See http://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html
static void runBeforeMain(void) __attribute__ ((constructor, used));
static void runBeforeMain(void) {
    _lock = [NSRecursiveLock new];
}

static void test(void) 
{
    static NSUInteger count = 0;
    NSLog(@"iteration #%lu", ++count);

    // WRONG: deadlock!
    //static dispatch_once_t token;
    //dispatch_once(&token, ^{
    //  test();
    //});

    // OK
    [_lock lock];
    test();
    [_lock unlock];

    --count;
}

int main(int argc, char **argv) {
    @autoreleasepool {
        test();
    }
    return EXIT_SUCCESS;
}

+initialize singleton

Using +initialize is an alternative idiom to create a singleton. Pros: It is several times faster than dispatch_once. Cons: +initialize is called once per class, so if you subclass the singleton, an instance will be created for each parent class too. Use it only if you know the singleton will not be subclassed.

static id sharedInstance;

+ (void) initialize {
    // subclassing would result in an instance per class, probably not what we want
    NSAssert([MySingleton class] == self, @"Subclassing is not welcome");
    sharedInstance = [[super alloc] initUniqueInstance];
}

+(instancetype) sharedInstance {
    return sharedInstance;
}
Jano
  • 62,815
  • 21
  • 164
  • 192
  • 1
    @jano i mean you are confused about self. The OP's use is correct - you are wrong. Although the OP's code is a mess there is no problem with self (or the way the sharedInstance is constructed). [self saveContext:] doesn't call an instance method at all. In a Class method 'self' refers to the Class. – hooleyhoop Jan 04 '13 at 12:01
  • Yes, my answer was blatantly wrong, I edited the top after your first comment. The singleton implementation is better with dispatch_once_t, no race conditions. – Jano Jan 04 '13 at 12:06
  • Using a shared instance for a singleton implementation isn't "better" in any way. I see no problems in using the class directly. If either way is any "better" or "worse" in order to implement a true singleton, using the class directly definitely is "better" than using a shared instance. – Fabian Kreiser Feb 08 '13 at 07:30
  • I didn't understand that sorry. In saying better referring to dispatch_once I mean that it guarantees that only one thread will execute the block at a time (though it still can deadlock if the same thread calls it again from inside the block). – Jano Feb 08 '13 at 15:10
  • This is a delightful extension of the singleton pattern. I didn't know about the compiler extensions to mark the methods as unavailable: I vastly prefer this to all the retain/release tricks normally performed in overriding alloc/init/etc. – cbowns May 23 '14 at 23:36