Синглтон с ARC - PullRequest
       35

Синглтон с ARC

14 голосов
/ 03 ноября 2011

У меня следующий вопрос: у меня есть объект синглтонного типа (я использую 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 Ответ

61 голосов
/ 03 ноября 2011

Ключевое слово self внутри метода ссылается на владельца метода, который является экземпляром объекта для методов экземпляра, и класс для методов класса.Тем не менее, сообщение saveContex отсутствует в конце (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;
}

+ initialize singleton

Использование + initialize - альтернативный способ создания синглтона.Плюсы: это в несколько раз быстрее, чем 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;
}
...