Ответ 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;
}