문제

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

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

부터 Kendall 게시됨 잠금 비용을 피하기 위해 시도하는 스레드 안전 싱글톤도 하나 던져볼까 생각했습니다.

#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 존재하지 않으면 인스턴스가 할당되고 비교 및 ​​교환('CAS')을 사용하여 인스턴스에 복사됩니다.

  3. 분쟁 사례:두 스레드가 모두 호출을 시도하는 경우 sharedInstance 동시에 그리고 sharedInstance 동시에 존재하지 않으면 싱글톤의 새 ​​인스턴스를 초기화하고 위치에 CAS를 시도합니다.어느 쪽이 이기든 CAS는 즉시 반환되고, 지는 쪽은 방금 할당한 인스턴스를 해제하고 (현재 설정된) 인스턴스를 반환합니다. sharedInstance.싱글 OSAtomicCompareAndSwapPtrBarrier 설정 스레드에 대한 쓰기 장벽과 테스트 스레드의 읽기 장벽 역할을 모두 수행합니다.

static MyClass *sharedInst = nil;

+ (id)sharedInstance
{
    @synchronize( self ) {
        if ( sharedInst == nil ) {
            /* sharedInst set up in init */
            [[self alloc] init];
        }
    }
    return sharedInst;
}

- (id)init
{
    if ( sharedInst != nil ) {
        [NSException raise:NSInternalInconsistencyException
            format:@"[%@ %@] cannot be called; use +[%@ %@] instead"],
            NSStringFromClass([self class]), NSStringFromSelector(_cmd), 
            NSStringFromClass([self class]),
            NSStringFromSelector(@selector(sharedInstance)"];
    } else if ( self = [super init] ) {
        sharedInst = self;
        /* Whatever class specific here */
    }
    return sharedInst;
}

/* These probably do nothing in
   a GC app.  Keeps singleton
   as an actual singleton in a
   non CG app
*/
- (NSUInteger)retainCount
{
    return NSUIntegerMax;
}

- (oneway void)release
{
}

- (id)retain
{
    return sharedInst;
}

- (id)autorelease
{
    return sharedInst;
}

편집 :이 구현은 ARC와 관련이 없습니다. 살펴보세요 ARC와 호환되는 객관적인 C 싱글 톤을 어떻게 구현합니까? 올바른 구현을 위해.

다른 답변에서 읽은 초기화의 모든 구현은 일반적인 오류를 공유합니다.

+ (void) initialize {
  _instance = [[MySingletonClass alloc] init] // <----- Wrong!
}

+ (void) initialize {
  if (self == [MySingletonClass class]){ // <----- Correct!
      _instance = [[MySingletonClass alloc] init] 
  }
}

Apple 문서는 초기화 블록에서 클래스 유형을 확인하는 것이 좋습니다. 서브 클래스는 기본적으로 초기화를 호출하기 때문입니다. KVO를 통해 하위 클래스가 간접적으로 생성 될 수있는 비 끔찍한 경우가 존재합니다. 다른 클래스에 다음 줄을 추가하면 :

[[MySingletonClass getInstance] addObserver:self forKeyPath:@"foo" options:0 context:nil]

Objective-C는 MySingletonClass의 서브 클래스를 암시 적으로 생성하여 두 번째 트리거링을 수행합니다. +initialize.

Initl 블록에서 중복 초기화를 암시 적으로 확인해야한다고 생각할 수 있습니다.

- (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.)

싱글 톤 매크로 코드에 대한 철저한 설명은 블로그에 있습니다.

http://cocoawithlove.com/2008/11/singletons-appdelegates-and-top-level.html.

ShareedInstance에 대한 흥미로운 변형이 있지만 스레드 안전하지만 초기화 후에도 잠기지 않습니다. 요청한대로 최고 답변을 수정하기에 충분한 확신은 없지만 추가 토론을 위해 발표합니다.

// Volatile to make sure we are not foiled by CPU caches
static volatile ALBackendRequestManager *sharedInstance;

// There's no need to call this directly, as method swizzling in sharedInstance
// means this will get called after the singleton is initialized.
+ (MySingleton *)simpleSharedInstance
{
    return (MySingleton *)sharedInstance;
}

+ (MySingleton*)sharedInstance
{
    @synchronized(self)
    {
        if (sharedInstance == nil)
        {
            sharedInstance = [[MySingleton alloc] init];
            // Replace expensive thread-safe method 
            // with the simpler one that just returns the allocated instance.
            SEL origSel = @selector(sharedInstance);
            SEL newSel = @selector(simpleSharedInstance);
            Method origMethod = class_getClassMethod(self, origSel);
            Method newMethod = class_getClassMethod(self, newSel);
            method_exchangeImplementations(origMethod, newMethod);
        }
    }
    return (MySingleton *)sharedInstance;
}

짧은 대답 : 훌륭합니다.

긴 대답 : 같은 ....

static SomeSingleton *instance = NULL;

@implementation SomeSingleton

+ (id) instance {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        if (instance == NULL){
            instance = [[super allocWithZone:NULL] init];
        }
    });
    return instance;
}

+ (id) allocWithZone:(NSZone *)paramZone {
    return [[self instance] retain];
}

- (id) copyWithZone:(NSZone *)paramZone {
    return self;
}

- (id) autorelease {
    return self;
}

- (NSUInteger) retainCount {
    return NSUIntegerMax;
}

- (id) retain {
    return self;
}

@end

반드시 읽으십시오 Dispatch/Once.h 헤더 무슨 일이 일어나고 있는지 이해합니다. 이 경우 헤더 댓글은 문서 나 사람 페이지보다 더 적용 할 수 있습니다.

싱글 톤을 수업에 넣었으므로 다른 수업은 싱글 톤 속성을 상속받을 수 있습니다.

Singleton.h :

static id sharedInstance = nil;

#define DEFINE_SHARED_INSTANCE + (id) sharedInstance {  return [self sharedInstance:&sharedInstance]; } \
                               + (id) allocWithZone:(NSZone *)zone { return [self allocWithZone:zone forInstance:&sharedInstance]; }

@interface Singleton : NSObject {

}

+ (id) sharedInstance;
+ (id) sharedInstance:(id*)inst;

+ (id) allocWithZone:(NSZone *)zone forInstance:(id*)inst;

@end

Singleton.m :

#import "Singleton.h"


@implementation Singleton


+ (id) sharedInstance { 
    return [self sharedInstance:&sharedInstance];
}

+ (id) sharedInstance:(id*)inst {
    @synchronized(self)
    {
        if (*inst == nil)
            *inst = [[self alloc] init];
    }
    return *inst;
}

+ (id) allocWithZone:(NSZone *)zone forInstance:(id*)inst {
    @synchronized(self) {
        if (*inst == nil) {
            *inst = [super allocWithZone:zone];
            return *inst;  // assignment and return on first allocation
        }
    }
    return nil; // on subsequent allocation attempts return nil
}

- (id)copyWithZone:(NSZone *)zone {
    return self;
}

- (id)retain {
    return self;
}

- (unsigned)retainCount {
    return UINT_MAX;  // denotes an object that cannot be released
}

- (void)release {
    //do nothing
}

- (id)autorelease {
    return self;
}


@end

그리고 여기 싱글 톤이되고 싶어하는 수업의 예가 있습니다.

#import "Singleton.h"

@interface SomeClass : Singleton {

}

@end

@implementation SomeClass 

DEFINE_SHARED_INSTANCE;

@end

싱글 톤 클래스에 대한 유일한 제한은 NSObject 서브 클래스라는 것입니다. 그러나 대부분의 경우 코드에서 싱글 톤을 사용하여 실제로는 nsobject 서브 클래스 이므로이 클래스는 내 인생을 편안하게하고 코드를 깨끗하게 만듭니다.

이것은 비가구 수집 된 환경에서도 작동합니다.

@interface MySingleton : NSObject {
}

+(MySingleton *)sharedManager;

@end


@implementation MySingleton

static MySingleton *sharedMySingleton = nil;

+(MySingleton*)sharedManager {
    @synchronized(self) {
        if (sharedMySingleton == nil) {
            [[self alloc] init]; // assignment not done here
        }
    }
    return sharedMySingleton;
}


+(id)allocWithZone:(NSZone *)zone {
    @synchronized(self) {
        if (sharedMySingleton == nil) {
            sharedMySingleton = [super allocWithZone:zone];
            return sharedMySingleton;  // assignment and return on first allocation
        }
    }
    return nil; //on subsequent allocation attempts return nil
}


-(void)dealloc {
    [super dealloc];
}

-(id)copyWithZone:(NSZone *)zone {
    return self;
}


-(id)retain {
    return self;
}


-(unsigned)retainCount {
    return UINT_MAX;  //denotes an object that cannot be release
}


-(void)release {
    //do nothing    
}


-(id)autorelease {
    return self;    
}


-(id)init {
    self = [super init];
    sharedMySingleton = self;

    //initialize here

    return self;
}

@end

이게 threadSafe가 아니며 첫 번째 호출 후 고가의 잠금을 피하십시오.

+ (MySingleton*)sharedInstance
{
    if (sharedInstance == nil) {
        @synchronized(self) {
            if (sharedInstance == nil) {
                sharedInstance = [[MySingleton alloc] init];
            }
        }
    }
    return (MySingleton *)sharedInstance;
}

여기 매크로가 있습니다 내가 합한 것 :

http://github.com/cjhanson/objective-c-optimized-singleton

그것은 기반입니다 Matt Gallagher의 작업그러나 사용 구현을 변경합니다 Google의 Dave MacLachlan에 의해 여기에 설명 된 방법 Swizzling.

의견 / 기여를 환영합니다.

어때

static MyClass *gInstance = NULL;

+ (MyClass *)instance
{
    if (gInstance == NULL) {
        @synchronized(self)
        {
            if (gInstance == NULL)
                gInstance = [[self alloc] init];
        }
    }

    return(gInstance);
}

따라서 초기화 후 동기화 비용을 피합니까?

Objective-C의 싱글 톤 패턴에 대한 심도있는 토론을 보려면 여기를 참조하십시오.

Objective-c에서 싱글 톤 패턴 사용

Klsingleton은 다음과 같습니다.

  1. 하위 분류 가능 (N-the Degree)
  2. 아크 호환
  3. 안전합니다 alloc 그리고 init
  4. 게으르게 적재
  5. 스레드 안전
  6. 잠금 장치
  7. 거시적
  8. 스위 즈 프리
  9. 단순한

Klsingleton

당신은 자기 객체가 아직 존재하지 않기 때문에 자기와 동기화하고 싶지 않습니다! 당신은 임시 ID 값을 잠그게됩니다. 다른 사람이 클래스 방법 (SharedInstance, Alloc, AllocwithZone :, 등)을 실행할 수 없도록하려면 클래스 객체에서 동기화해야합니다.

@implementation MYSingleton

static MYSingleton * sharedInstance = nil;

+( id )sharedInstance {
    @synchronized( [ MYSingleton class ] ) {
        if( sharedInstance == nil )
            sharedInstance = [ [ MYSingleton alloc ] init ];
    }

    return sharedInstance;
}

+( id )allocWithZone:( NSZone * )zone {
    @synchronized( [ MYSingleton class ] ) {
        if( sharedInstance == nil )
            sharedInstance = [ super allocWithZone:zone ];
    }

    return sharedInstance;
}

-( id )init {
    @synchronized( [ MYSingleton class ] ) {
        self = [ super init ];
        if( self != nil ) {
            // Insert initialization code here
        }

        return self;
    }
}

@end

나는 이것을 여기에두고 싶었다. 그래서 나는 그것을 잃지 않는다. 이것의 장점은 인터페이스 빌더에서 사용할 수 있다는 것입니다. 이는 큰 이점입니다. 이것은 내가 요청한 또 다른 질문에서 가져온 것입니다:

static Server *instance;

+ (Server *)instance { return instance; }

+ (id)hiddenAlloc
{
    return [super alloc];
}

+ (id)alloc
{
    return [[self instance] retain];
}


+ (void)initialize
{
    static BOOL initialized = NO;
    if(!initialized)
    {
        initialized = YES;
        instance = [[Server hiddenAlloc] init];
    }
}

- (id) init
{
    if (instance)
        return self;
    self = [super init];
    if (self != nil) {
        // whatever
    }
    return self;
}
static mySingleton *obj=nil;

@implementation mySingleton

-(id) init {
    if(obj != nil){     
        [self release];
        return obj;
    } else if(self = [super init]) {
        obj = self;
    }   
    return obj;
}

+(mySingleton*) getSharedInstance {
    @synchronized(self){
        if(obj == nil) {
            obj = [[mySingleton alloc] init];
        }
    }
    return obj;
}

- (id)retain {
    return self;
}

- (id)copy {
    return self;
}

- (unsigned)retainCount {
    return UINT_MAX;  // denotes an object that cannot be released
}

- (void)release {
    if(obj != self){
        [super release];
    }
    //do nothing
}

- (id)autorelease {
    return self;
}

-(void) dealloc {
    [super dealloc];
}
@end

나는이 "질문"에 대한 많은 의견이 있다는 것을 알고 있지만, 싱글 톤을 정의하기 위해 매크로를 사용하는 것을 제안하는 많은 사람들이 보이지 않습니다. 그것은 일반적인 패턴이며 매크로는 싱글 톤을 크게 단순화합니다.

다음은 내가 본 몇 가지 OBJC 구현을 기반으로 작성한 매크로입니다.

Singeton.h

/**
 @abstract  Helps define the interface of a singleton.
 @param  TYPE  The type of this singleton.
 @param  NAME  The name of the singleton accessor.  Must match the name used in the implementation.
 @discussion
 Typcially the NAME is something like 'sharedThing' where 'Thing' is the prefix-removed type name of the class.
 */
#define SingletonInterface(TYPE, NAME) \
+ (TYPE *)NAME;


/**
 @abstract  Helps define the implementation of a singleton.
 @param  TYPE  The type of this singleton.
 @param  NAME  The name of the singleton accessor.  Must match the name used in the interface.
 @discussion
 Typcially the NAME is something like 'sharedThing' where 'Thing' is the prefix-removed type name of the class.
 */
#define SingletonImplementation(TYPE, NAME) \
static TYPE *__ ## NAME; \
\
\
+ (void)initialize \
{ \
    static BOOL initialized = NO; \
    if(!initialized) \
    { \
        initialized = YES; \
        __ ## NAME = [[TYPE alloc] init]; \
    } \
} \
\
\
+ (TYPE *)NAME \
{ \
    return __ ## NAME; \
}

사용의 예 :

MyManager.h

@interface MyManager

SingletonInterface(MyManager, sharedManager);

// ...

@end

MyManager.m

@implementation MyManager

- (id)init
{
    self = [super init];
    if (self) {
        // Initialization code here.
    }

    return self;
}

SingletonImplementation(MyManager, sharedManager);

// ...

@end

거의 비어있을 때 왜 인터페이스 매크로가 있습니까? 헤더와 코드 파일 간의 코드 일관성; 더 많은 자동 방법을 추가하거나 변경하려는 경우 유지 가능성.

초기화 방법을 사용하여 여기에서 가장 인기있는 답변에 사용되는 싱글 톤을 작성합니다 (글을 쓰는 시간).

객관적인 C 클래스 방법을 사용하면 싱글 톤 패턴을 일반적인 방법으로 사용하지 않을 수 있습니다.

[[Librarian sharedInstance] openLibrary]

에게:

[Librarian openLibrary]

다른 클래스 안에 수업을 감싸서 수업 방법, 인스턴스를 생성하지 않기 때문에 우연히 중복 인스턴스를 만들 가능성이 없습니다!

더 자세한 블로그를 썼습니다 여기 :)

@Robbie-Hanson에서 예제를 확장하려면 ...

static MySingleton* sharedSingleton = nil;

+ (void)initialize {
    static BOOL initialized = NO;
    if (!initialized) {
        initialized = YES;
        sharedSingleton = [[self alloc] init];
    }
}

- (id)init {
    self = [super init];
    if (self) {
        // Member initialization here.
    }
    return self;
}

내 방식은 다음과 같이 간단합니다.

static id instanceOfXXX = nil;

+ (id) sharedXXX
{
    static volatile BOOL initialized = NO;

    if (!initialized)
    {
        @synchronized([XXX class])
        {
            if (!initialized)
            {
                instanceOfXXX = [[XXX alloc] init];
                initialized = YES;
            }
        }
    }

    return instanceOfXXX;
}

싱글 톤이 이미 초기화되면 잠금 블록이 입력되지 않습니다. 두 번째 확인 (! 초기화)은 현재 스레드가 잠금을 획득 할 때 아직 초기화되지 않은지 확인하는 것입니다.

나는 모든 솔루션을 읽지 않았 으므로이 코드가 중복되면 용서하십시오.

이것은 제 생각에 가장 스레드 안전 구현입니다.

+(SingletonObject *) sharedManager
{
    static SingletonObject * sharedResourcesObj = nil;

    @synchronized(self)
    {
        if (!sharedResourcesObj)
        {
            sharedResourcesObj = [[SingletonObject alloc] init];
        }
    }

    return sharedResourcesObj;
}

나는 보통 Ben Hoffstein의 답변과 거의 비슷한 코드를 사용합니다 (Wikipedia에서도 나왔습니다). 나는 Chris Hanson이 그의 의견으로 언급 한 이유로 그것을 사용합니다.

그러나 때로는 싱글 톤을 펜촉에 넣을 필요가 있으며이 경우 다음을 사용합니다.

@implementation Singleton

static Singleton *singleton = nil;

- (id)init {
    static BOOL initialized = NO;
    if (!initialized) {
        self = [super init];
        singleton = self;
        initialized = YES;
    }
    return self;
}

+ (id)allocWithZone:(NSZone*)zone {
    @synchronized (self) {
        if (!singleton)
            singleton = [super allocWithZone:zone];     
    }
    return singleton;
}

+ (Singleton*)sharedSingleton {
    if (!singleton)
        [[Singleton alloc] init];
    return singleton;
}

@end

나는 구현을 떠난다 -retain (등) 독자에게는 위의 코드가 쓰레기 수집 된 환경에서 필요한 전부입니다.

허용 된 답변은 컴파일하지만 부정확합니다.

+ (MySingleton*)sharedInstance
{
    @synchronized(self)  <-------- self does not exist at class scope
    {
        if (sharedInstance == nil)
            sharedInstance = [[MySingleton alloc] init];
    }
    return sharedInstance;
}

Apple 문서 :

... 비슷한 접근 방식을 취하기 위해 자신 대신 클래스 객체를 사용하여 관련 클래스의 클래스 메소드를 동기화 할 수 있습니다.

셀프 작업을 사용하더라도, 그것은해서는 안되며 이것은 사본과 함께 나에게 붙여 넣은 실수처럼 보입니다. 클래스 공장 방법에 대한 올바른 구현은 다음과 같습니다.

+ (MySingleton*)getInstance
{
    @synchronized([MySingleton class]) 
    {
        if (sharedInstance == nil)
            sharedInstance = [[MySingleton alloc] init];
    }
    return sharedInstance;
}
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top