Ключевое слово 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;
}