sourcecode

내 Objective-C 싱글톤은 어떤 모습이어야 할까요?

copyscript 2023. 4. 19. 23:23
반응형

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

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. if : If 면면sharedInstance않는CAS')을하여 할당 및됩니다.CAS'는 Compare And Swap('CAS')이다.

  3. 2개 콜을 시행하는 경우sharedInstance동시에 sharedInstance동시에 존재하지 않는 경우, 양쪽 모두 싱글톤의 새로운 인스턴스를 초기화하고 CAS를 제 위치에 배치하려고 합니다.이 CAS가 , 되면, 설정된) CAS가 됩니다.sharedInstance ★★★★★★★★★★★★★★★.OSAtomicCompareAndSwapPtrBarrier는 설정 스레드의 쓰기 장벽과 테스트 스레드의 읽기 장벽으로 기능합니다.

정적 MyClass *sharedInst = 0;

+ (id)shared Instance{@synchronize(self) {if ( sharedInst == nill )의 경우,/* sharedInst는 init */로 셋업됩니다.[[self allocate]init];
}}sharedInst를 반환한다.}
- (id)init{if ( sharedInst != nill ) {[NSException 상승:NSInternalInconsistencyException(NSInternalInconsistencyEx형식:@"[%@%@]을(를) 호출할 수 없습니다.대신 +[%@%@]를 사용합니다." ,NSString From Class([셀프 클래스]), NSString From Selector(_cmd),NSString From Class([셀프 클래스]),NSString From Selector(@selector(shared Instance))";
} else if ( self = [ super init ]) {sharedInst = 자체;
/* 여기에 특정 클래스가 있는 경우}sharedInst를 반환한다.}
/* 이것들은 아마GC 앱싱글톤 유지실제로 싱글톤으로서비 CG 앱*/- (NSUIInterger)retain Count{NSIntergerMax를 반환한다.}
- (단방향 보이드) 해제{}
- (id) 표시{sharedInst를 반환한다.}
- (id)자동 리스{sharedInst를 반환한다.}

편집: 이 실장은 ARC에 의해 폐지되었습니다.올바른 구현을 위해 ARC와 호환되는 Objective-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.

다음과 같이 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만으로 대체하십시오.

싱글톤 매크로 코드에 대한 자세한 설명은 코코아 위드 러브 블로그에 있습니다.

http://cocoawithlove.com/2008/11/singletons-appdelegates-and-top-level.html 를 참조해 주세요.

스레드는 안전하지만 초기화 후 잠기지 않는 흥미로운 shared Instance가 있습니다.요청하신 대로 상위 답변을 수정할 수 있을지는 아직 확실하지 않지만, 자세한 논의를 위해 제안합니다.

// 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 헤더를 읽고 무슨 일이 일어나고 있는지 확인하십시오.이 경우 헤더 코멘트는 docs 페이지 또는 man 페이지보다 더 적합합니다.

다른 클래스가 싱글톤 속성을 상속할 수 있도록 싱글톤을 클래스로 만들었습니다.

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

첫 번째 통화 후 잠금장치 고가의 사용을 피하고 스레드 세이프해야 하지 않을까요?

+ (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이 기술한 바와 같이 메서드 스위즐링을 사용하도록 구현을 변경합니다.

코멘트/기고를 환영합니다.

어때.

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번째까지)
  2. ARC 대응
  3. alloc ★★★★★★★★★★★★★★★★★」init
  4. 로딩이 느리다
  5. 스레드 세이프
  6. 잠금 없음(@synchronize가 아닌 +초기화 사용)
  7. 매크로 프리
  8. 스위즐 프리
  9. 간단하죠.

KLSingleton

자기와 동기화하기 싫으면...아직 자기 물체는 존재하지 않기 때문에!임시 ID 값으로 잠기게 됩니다.클래스 메서드(sharedInstance, allocate, allocateWithZone: 등)를 다른 사용자가 실행할 수 없도록 해야 하므로 클래스 개체로 동기화해야 합니다.

@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

잃어버리지 않게 그냥 여기에 놔두고 싶었어요이 제품의 장점은 Interface Builder에서 사용할 수 있다는 것입니다. 이는 엄청난 이점입니다.이것은 제가 질문한 다른 질문에서 가져온 입니다.

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

거의 비어 있는데 인터페이스 매크로가 왜 필요합니까?헤더와 코드 파일 간의 코드 일관성: 자동 메서드를 추가하거나 변경할 경우 유지 보수성.

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

Objective C 클래스 메서드에서는 다음과 같은 방법으로 싱글톤 패턴을 사용하지 않도록 할 수 있습니다.

[[Librarian sharedInstance] openLibrary]

대상:

[Librarian openLibrary]

Class Methods만 있는 다른 클래스 내에서 클래스를 래핑하면 인스턴스를 만들지 않기 때문에 실수로 중복 인스턴스를 만들 가능성이 없습니다.

자세한 블로그는 이쪽에서 작성했습니다.

예를 @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;
}

싱글톤이 이미 초기화되어 있는 경우 LOCK 블록은 입력되지 않습니다.두 번째 체크는 if(!initialized)가 현재 스레드가 LOCK을 취득했을 때 아직 초기화되지 않았는지 확인하는 것입니다.

모든 해결책을 다 읽은 것은 아니기 때문에 이 코드가 중복되어도 양해 바랍니다.

내 생각에 이것이 가장 안전한 구현이다.

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

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

    return sharedResourcesObj;
}

나는 보통 Ben Hoffstein의 대답과 거의 비슷한 코드를 사용한다(Wikipedia에서도 그것을 얻었다).나는 Chris Hanson이 그의 코멘트에서 말한 이유로 그것을 사용한다.

단, 싱글톤을 NIB에 넣을 필요가 있는 경우가 있습니다.이 경우 다음을 사용합니다.

@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 문서별:

... self 대신 Class 개체를 사용하여 연관된 클래스의 클래스 메서드를 동기화할 수 있습니다.

셀프워크를 사용해도 안 되고 복사 붙여넣기 실수처럼 보입니다.클래스 팩토리 방식의 올바른 실장은 다음과 같습니다.

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

언급URL : https://stackoverflow.com/questions/145154/what-should-my-objective-c-singleton-look-like

반응형