Синглтон с ARC

Мой вопрос следующий: у меня есть объект singleton type (я использую ARC), который имеет этот код в файле реализации

+(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:
}

Почему? Метод объявлен в файле .h с помощью +... модель getManagedContext не дает этой ошибки????

Ответ 1

Ключевое слово self внутри метода ссылается на владельца метода, который является экземпляром объекта для методов экземпляра, и класса для методов класса. Однако сообщение saveContex отсутствует в конце t (saveContext).

dispatch_once singleton

И вот лучшая синглтонная идиома, совместимая с ARC:

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

Тот же код, что и шаблон Xcode

Тот же код, что и шаблон Xcode с заполнителями:

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

Тот же код + отключен alloc/init/new

Хотите узнать пользователей, что они должны вызывать sharedInstance вместо alloc/init/new? Вы можете отключить методы с атрибутом недоступным. Это вызовет ошибку компилятора, если какой-либо из этих методов вызывается в классе.

#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

Предупреждение: dispatch_once не является реентерабельным

Не делайте рекурсивный вызов sharedInstance из блока dispatch_once.

Если вы вызываете dispatch_once из нескольких потоков, он будет действовать как барьер, препятствующий одновременному доступу. Но, если вы вызовете его снова в том же потоке изнутри блока, он закроет поток. Этот пример иллюстрирует проблему:

#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;
}

+ инициализировать singleton

Использование + initialize является альтернативной идиомой для создания singleton. Плюсы: это в несколько раз быстрее, чем dispatch_once. Минусы: +initialize вызывается один раз для каждого класса, поэтому, если вы подклассифицируете singleton, экземпляр будет создан для каждого родительского класса. Используйте его, только если вы знаете, что синглтон не будет подклассифицирован.

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;
}