[objective-c] Objective-C 싱글 톤은 어떻게 생겼나요? [닫은]

내 싱글 톤 접근 자 방법은 일반적으로 다음과 같은 변형입니다.

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

자, 이것이 어떻게 작동하는지 설명하겠습니다.

  1. 빠른 경우 : 정상 실행 sharedInstance에서 이미 설정되었으므로 while루프가 실행되지 않고 단순히 변수의 존재를 테스트 한 후에 함수가 반환됩니다.

  2. 느린 경우 : sharedInstance존재하지 않는 경우 인스턴스는 Compare And Swap ( ‘CAS’)을 사용하여 할당되고 복사됩니다.

  3. 경합 경우 : 두 개의 스레드 호출에 모두 시도하면 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 .