내 싱글 톤 접근 자 방법은 일반적으로 다음과 같은 변형입니다.
static MyClass *gInstance = NULL;
+ (MyClass *)instance
{
@synchronized(self)
{
if (gInstance == NULL)
gInstance = [[self alloc] init];
}
return(gInstance);
}
이것을 개선하기 위해 무엇을 할 수 있습니까?
답변
다른 옵션은이 +(void)initialize
방법 을 사용하는 것입니다. 설명서에서 :
런타임은
initialize
클래스 바로 전에 한 번만 프로그램의 각 클래스로 보내 거나 클래스에서 상속 된 모든 클래스가 프로그램 내에서 첫 번째 메시지를 보냅니다. 클래스가 사용되지 않으면 메소드가 호출되지 않을 수 있습니다. 런타임은initialize
스레드에 안전한 방식으로 메시지를 클래스에 보냅니다 . 수퍼 클래스는 서브 클래스 전에이 메시지를 수신합니다.
따라서 이와 비슷한 것을 할 수 있습니다.
static MySingleton *sharedSingleton;
+ (void)initialize
{
static BOOL initialized = NO;
if(!initialized)
{
initialized = YES;
sharedSingleton = [[MySingleton alloc] init];
}
}
답변
@interface MySingleton : NSObject
{
}
+ (MySingleton *)sharedSingleton;
@end
@implementation MySingleton
+ (MySingleton *)sharedSingleton
{
static MySingleton *sharedSingleton;
@synchronized(self)
{
if (!sharedSingleton)
sharedSingleton = [[MySingleton alloc] init];
return sharedSingleton;
}
}
@end
답변
아래의 다른 답변에 따라, 당신이해야한다고 생각합니다.
+ (id)sharedFoo
{
static dispatch_once_t once;
static MyFoo *sharedFoo;
dispatch_once(&once, ^ { sharedFoo = [[self alloc] init]; });
return sharedFoo;
}
답변
이후 켄달 게시 비용을 잠금 방지하기 위해 그 시도 싱글 스레드 세이프를, 내가 아니라 하나를 던져 것이라고 생각 :
#import <libkern/OSAtomic.h>
static void * volatile sharedInstance = nil;
+ (className *) sharedInstance {
while (!sharedInstance) {
className *temp = [[self alloc] init];
if(!OSAtomicCompareAndSwapPtrBarrier(0x0, temp, &sharedInstance)) {
[temp release];
}
}
return sharedInstance;
}
자, 이것이 어떻게 작동하는지 설명하겠습니다.
-
빠른 경우 : 정상 실행
sharedInstance
에서 이미 설정되었으므로while
루프가 실행되지 않고 단순히 변수의 존재를 테스트 한 후에 함수가 반환됩니다. -
느린 경우 :
sharedInstance
존재하지 않는 경우 인스턴스는 Compare And Swap ( ‘CAS’)을 사용하여 할당되고 복사됩니다. -
경합 경우 : 두 개의 스레드 호출에 모두 시도하면
sharedInstance
같은 시간에 와sharedInstance
같은 시간에 존재하지 않는 다음 그들이 싱글 모두 초기화 새로운 인스턴스와 위치에 CAS 그것을 시도합니다. 어느 쪽이 CAS에 당첨 되든 즉시 반환, 둘 중 하나는 방금 할당 한 인스턴스를 해제하고 (현재 설정)을 반환합니다sharedInstance
. 단일OSAtomicCompareAndSwapPtrBarrier
은 설정 스레드의 쓰기 장벽과 테스트 스레드의 읽기 장벽 역할을합니다.
답변
정적 MyClass * sharedInst = nil; + (id) shared 인스턴스 { @synchronize (자체) { if (sharedInst == nil) { / * sharedInst 설정 init * / [[자체 할당] init]; } } return sharedInst; } -(ID) 초기화 { if (sharedInst! = nil) { [NSException raise : NSInternalInconsistencyException 형식 : @ "[% @ % @]을 (를) 호출 할 수 없습니다. 대신 + [% @ % @] 사용"], NSStringFromClass ([self class]), NSStringFromSelector (_cmd), NSStringFromClass ([자체 클래스]), NSStringFromSelector (@selector (sharedInstance) "]; } else if (self = [super init]) { sharedInst = 자기; / * 여기에 특정 클래스가 무엇이든 * / } return sharedInst; } / * 이것들은 아마 아무것도하지 않습니다 GC 앱. 싱글 톤 유지 실제 싱글 톤으로 비 CG 앱 * / -(NSUInteger) 유지 횟수 { NSUIntegerMax를 반환합니다. } -(일방 통행) 해제 { } -(id) 유지 { return sharedInst; } -(ID) 자동 출시 { return sharedInst; }
답변
편집 :이 구현은 ARC에서 더 이상 사용되지 않습니다. ARC와 호환되는 Objective-C 싱글 톤을 어떻게 구현합니까?를 살펴보십시오 . 올바른 구현을 위해.
다른 답변에서 읽은 초기화의 모든 구현은 일반적인 오류를 공유합니다.
+ (void) initialize {
_instance = [[MySingletonClass alloc] init] // <----- Wrong!
}
+ (void) initialize {
if (self == [MySingletonClass class]){ // <----- Correct!
_instance = [[MySingletonClass alloc] init]
}
}
Apple 설명서는 초기화 블록에서 클래스 유형을 확인하도록 권장합니다. 서브 클래스는 기본적으로 initialize를 호출하기 때문입니다. KVO를 통해 서브 클래스를 간접적으로 생성 할 수있는 명백한 경우가 있습니다. 다른 클래스에 다음 줄을 추가하는 경우 :
[[MySingletonClass getInstance] addObserver:self forKeyPath:@"foo" options:0 context:nil]
Objective-C는 내재적으로 MySingletonClass의 서브 클래스를 작성하여 두 번째 트리거를 발생 +initialize
시킵니다.
다음과 같이 init 블록에서 중복 초기화를 암시 적으로 확인해야한다고 생각할 수 있습니다.
- (id) init { <----- Wrong!
if (_instance != nil) {
// Some hack
}
else {
// Do stuff
}
return self;
}
그러나 당신은 발에 자신을 쏠 것입니다; 또는 다른 개발자에게 발을 쏠 기회를 줄 수도 있습니다.
- (id) init { <----- Correct!
NSAssert(_instance == nil, @"Duplication initialization of singleton");
self = [super init];
if (self){
// Do stuff
}
return self;
}
TL; DR, 여기 내 구현입니다
@implementation MySingletonClass
static MySingletonClass * _instance;
+ (void) initialize {
if (self == [MySingletonClass class]){
_instance = [[MySingletonClass alloc] init];
}
}
- (id) init {
ZAssert (_instance == nil, @"Duplication initialization of singleton");
self = [super init];
if (self) {
// Initialization
}
return self;
}
+ (id) getInstance {
return _instance;
}
@end
ZAssert를 자체 어설 션 매크로 또는 NSAssert로 바꿉니다.
답변
싱글 톤 매크로 코드에 대한 자세한 설명은 블로그 Cocoa With Love에 있습니다.
http://cocoawithlove.com/2008/11/singletons-appdelegates-and-top-level.html .