Objective-C에서 GCD의 dispatch_once를 사용하여 싱글 톤 만들기


341

iOS 4.0 이상을 타겟팅 할 수있는 경우

GCD를 사용하면 Objective-C (스레드 안전)에서 싱글 톤을 만드는 가장 좋은 방법입니까?

+ (instancetype)sharedInstance
{
    static dispatch_once_t once;
    static id sharedInstance;
    dispatch_once(&once, ^{
        sharedInstance = [[self alloc] init];
    });
    return sharedInstance;
}

2
클래스 사용자가 alloc / copy를 호출하지 못하게하는 방법이 있습니까?
Nicolas Miari 2016 년

3
dispatch_once_t 및 dispatch_once는 4.1이 아닌 4.0에 도입 된 것으로 보입니다 ( developer.apple.com/library/ios/#documentation/Performance/… 참조 )
Ben Flynn

1
이 방법은 init에 singleton 객체를 사용해야하는 경우 문제가됩니다. Matt Gallagher의 코드는 몇 번 이상 나를 위해 일했습니다. cocoawithlove.com/2008/11/…
greg

1
이 예제에서는 그 결과가 중요하지 않다는 것을 알고 있습니다. 하지만 사람들이 '새'를 더 사용하지 않는 이유는 무엇입니까? dispatch_once (& once, ^ {sharedInstance = [self new];}는 조금 더 깔끔해 보입니다. alloc + init와 같습니다
Chris Hatton

3
반환 유형을 사용하여 시작하십시오 instancetype. 대신 코드를 사용하면 코드 완성이 훨씬 좋습니다 id.
Mr Rogers

답변:


215

이것은 클래스의 인스턴스를 만드는 완벽하고 수용 가능한 스레드 안전 방법입니다. 기술적으로 "단일"이 아닐 수도 있지만 (이러한 개체 중 하나만있을 수 있음) 개체 [Foo sharedFoo]에 액세스하는 방법 만 사용하면 충분합니다.


4
그래도 어떻게 해제합니까?
samvermette

65
@samvermette 당신은하지 않습니다. 싱글 톤의 요점은 항상 존재한다는 것입니다. 따라서 해제하지 않고 프로세스가 종료되면 메모리가 회수됩니다.
Dave DeLong 2016 년

6
@ 데이브 DeLong : 내 의견으로는 싱글 톤을 갖는 것은 불멸의 확실성이 아니라 우리가 하나의 인스턴스를 가지고 있다는 확신입니다. 그 싱글 톤이 세마포어를 줄이면 어떻게 될까요? 항상 존재한다고 자의적으로 말할 수는 없습니다.
jacekmigacz

4
@hooleyhoop 예, 설명서에 . "여러 스레드에서 동시에 호출되면이 함수는 블록이 완료 될 때까지 동 기적으로 대기합니다."
Kevin

3
WalterMartinVargas - 페 @ 강한 기준은 정적 변수가 유지된다
데이브 드롱

36

인스턴스 유형

instancetype 에 대한 많은 언어 확장 중 하나입니다 Objective-C 새로운 릴리스마다 추가됩니다.

그것을 알고 사랑하십시오.

또한 하위 수준의 세부 사항에주의를 기울이면 Objective-C를 변환하는 강력하고 새로운 방법에 대한 통찰력을 얻을 수있는 방법을 예로 들어 보겠습니다.

여기를 참조하십시오 : instancetype


+ (instancetype)sharedInstance
{
    static dispatch_once_t once;
    static id sharedInstance;

    dispatch_once(&once, ^
    {
        sharedInstance = [self new];
    });    
    return sharedInstance;
}

+ (Class*)sharedInstance
{
    static dispatch_once_t once;
    static Class *sharedInstance;

    dispatch_once(&once, ^
    {
        sharedInstance = [self new];
    });    
    return sharedInstance;
}

4
놀라운 팁, 감사합니다! instancetype은 메소드가 관련 결과 유형을 리턴 함을 알리기 위해 결과 유형으로 사용할 수있는 컨텍스트 키워드입니다. ... instancetype을 사용하면 컴파일러에서 형식을 올바르게 유추합니다.
Fattie

1
두 스 니펫이 여기에서 무엇을 의미하는지는 분명하지 않습니다. 하나는 다른 것보다 바람직합니까? 저자가 이것에 대한 약간의 설명을 추가 할 수 있다면 좋을 것입니다.
galactica

33

MySingleton.h

@interface MySingleton : NSObject

+(instancetype)sharedInstance;

+(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")));
-(instancetype)copy __attribute__((unavailable("copy not available, call sharedInstance instead")));

@end

MySingleton.m

@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

init를 어떻게 사용할 수 없습니까? 적어도 하나는 사용할 수 init없습니까?
Honey

2
싱글 톤에는 액세스 포인트가 하나만 있어야합니다. 그리고이 시점은 sharedInstance입니다. * .h 파일에 init 메소드가 있으면 다른 싱글 톤 인스턴스를 작성할 수 있습니다. 이것은 싱글 톤의 정의와 모순됩니다.
Sergey Petruk

1
@ asma22 __attribute __ ((unavailable ())은 이러한 메소드를 사용할 수 없습니다. 다른 프로그래머가 사용할 수없는 것으로 표시된 메소드를 사용하려는 경우 오류가 발생합니다.
Sergey Petruk

1
나는 완전히 얻었고 나는 새로운 것을 배웠습니다. 당신의 대답에는 아무 문제가 없습니다. 초보자에게는 약간 혼란 스러울 수도 있습니다 ...
Honey

1
이것은 MySingleton예를 들어 MySingleton.m전화하는 경우에만 작동합니다 .[super alloc]
Sergey Petruk

6

alloc 메소드를 겹쳐 쓰면서 클래스를 할당하지 않도록 할 수 있습니다.

@implementation MyClass

static BOOL useinside = NO;
static id _sharedObject = nil;


+(id) alloc {
    if (!useinside) {
        @throw [NSException exceptionWithName:@"Singleton Vialotaion" reason:@"You are violating the singleton class usage. Please call +sharedInstance method" userInfo:nil];
    }
    else {
        return [super alloc];
    }
}

+(id)sharedInstance
{
    static dispatch_once_t p = 0;
    dispatch_once(&p, ^{
        useinside = YES;
        _sharedObject = [[MyClass alloc] init];
        useinside = NO;
    });   
    // returns the same object each time
    return _sharedObject;
}

1
위의 의견에서 내 질문에 대답합니다. 내가 방어 프로그래밍에 너무 많은 것은 아니지만 ...
니콜라 Miari

5

Dave는 정확합니다. 완벽합니다. 당신은 체크 아웃 할 수 있습니다 싱글 만드는 방법에 대한 애플의 워드 프로세서 클래스는 sharedFoo 방법을 사용하지 않을 경우에만 하나를 창조 할 수 있는지 확인하기 위해 다른 방법의 일부를 구현하는 방법에 대한 팁을.


8
어 ... 그건 싱글 톤을 만드는 가장 좋은 예가 아닙니다. 메모리 관리 방법을 재정의 할 필요는 없습니다.
Dave DeLong

19
ARC를 사용하면 완전히 유효하지 않습니다.
logancautrell

인용 된 문서는 폐기되었습니다. 또한 외부 콘텐츠에만 링크되는 답변은 일반적으로 SO 답변이 좋지 않습니다. 귀하의 답변에서 최소한 관련 부분을 발췌하십시오. 옛날 방식으로 후손을 구하기를 원하지 않는 한 여기에서 귀찮게하지 마십시오.
toolbear

4

[[MyClass alloc] init]이 sharedInstance와 동일한 객체를 반환하도록하려면 (필자의 의견으로는 필요하지 않지만 일부 사람들은 원하는 경우) 두 번째 dispatch_once를 사용하여 매우 쉽고 안전하게 수행 할 수 있습니다.

- (instancetype)init
{
    static dispatch_once_t once;
    static Class *sharedInstance;

    dispatch_once(&once, ^
    {
        // Your normal init code goes here. 
        sharedInstance = self;
    });

    return sharedInstance;
}

이것은 [[MyClass alloc] init]와 [MyClass sharedInstance]의 조합이 동일한 객체를 반환 할 수있게합니다. [MyClass sharedInstance]는 조금 더 효율적입니다. 작동 방식 : [MyClass sharedInstance]는 [[MyClass alloc] init]를 한 번 호출합니다. 다른 코드에서도 여러 번 호출 할 수 있습니다. init의 첫 번째 호출자는 "정상적인"초기화를 수행하고 싱글 톤 객체를 init 메소드에 저장합니다. 나중에 init을 호출하면 alloc이 리턴 한 내용을 완전히 무시하고 동일한 sharedInstance를 리턴합니다. alloc의 결과는 할당 해제됩니다.

+ sharedInstance 메소드는 항상 그렇듯이 작동합니다. [[MyClass alloc] init]를 호출 한 첫 번째 호출자가 아닌 경우 init의 결과는 alloc 호출의 결과가 아니지만 괜찮습니다.


2

이것이 "싱글 톤을 만드는 가장 좋은 방법"인지 묻습니다.

몇 가지 생각 :

  1. 첫째, 이것은 스레드 안전 솔루션입니다. 이 dispatch_once패턴은 Objective-C에서 싱글 톤을 생성하는 현대적이고 안전한 스레드 방식입니다. 걱정하지 마십시오.

  2. 그러나 이것이 최선의 방법인지 물었다. 그러나 싱글 톤과 함께 사용 하면 instancetype[[self alloc] init]오해의 가능성 이 있음을 인정해야합니다 .

    instancetype그것의 장점은 id우리가 어제 해왔 던 것처럼 유형에 의지하지 않고 클래스를 서브 클래 싱 할 수 있음을 선언하는 명백한 방법이라는 것입니다 .

    그러나이 static방법에서는 서브 클래 싱 문제를 제시합니다. 자체 메소드 를 구현하지 않고 수퍼 클래스의 서브 클래스 인 경우 ImageCacheBlobCache싱글 톤은 어떻습니까?CachesharedCache

    ImageCache *imageCache = [ImageCache sharedCache];  // fine
    BlobCache *blobCache = [BlobCache sharedCache];     // error; this will return the aforementioned ImageCache!!!

    이것이 작동하기 위해서는 서브 클래스가 자신 만의 sharedInstance(또는 특정 클래스에 대해 호출하는) 메소드를 구현해야 합니다.

    결론적으로, 원본 sharedInstance 하위 클래스를 지원하는 것처럼 보이지만 그렇지 않습니다. 서브 클래 싱을 지원하려는 경우 최소한 미래 개발자에게이 방법을 재정의해야한다고 경고하는 설명서가 포함되어야합니다.

  3. Swift와의 최상의 상호 운용성을 위해 클래스 메소드가 아닌 속성으로 정의하려는 경우가 있습니다. 예 :

    @interface Foo : NSObject
    @property (class, readonly, strong) Foo *sharedFoo;
    @end

    그런 다음이 속성에 대한 getter를 작성할 수 있습니다 (구현은 dispatch_once제안한 패턴을 사용합니다 ).

    + (Foo *)sharedFoo { ... }

    이것의 장점은 Swift 사용자가 그것을 사용하면 다음과 같이 할 수 있다는 것입니다.

    let foo = Foo.shared

    ()속성으로 구현했기 때문에 는 없습니다 . Swift 3부터는 싱글 톤에 일반적으로 액세스하는 방법이 있습니다. 따라서 속성으로 정의하면 상호 운용성을 촉진하는 데 도움이됩니다.

    따로, Apple이 싱글 톤을 정의하는 방법을 살펴보면, 이것이 싱글턴을 정의한 패턴입니다. 예를 들어 NSURLSession싱글 톤은 다음과 같이 정의됩니다.

    @property (class, readonly, strong) NSURLSession *sharedSession;
  4. 매우 작은 스위프트 상호 운용성 고려 사항은 싱글 톤의 이름이었습니다. 유형이 아닌 이름을 통합 할 수있는 것이 가장 좋습니다 sharedInstance. 예를 들어 클래스가 Foo인 경우 singleton 속성을로 정의 할 수 있습니다 sharedFoo. 또는 클래스가 있으면 DatabaseManager속성을 호출 할 수 있습니다 sharedManager. 그런 다음 Swift 사용자는 다음을 수행 할 수 있습니다.

    let foo = Foo.shared
    let manager = DatabaseManager.shared

    분명히을 사용하려는 경우 언제든지 원하는 sharedInstance스위프트 이름을 선언 할 수 있습니다.

    @property (class, readonly, strong) Foo* sharedInstance NS_SWIFT_NAME(shared);

    분명히 Objective-C 코드를 작성할 때 Swift 상호 운용성이 다른 디자인 고려 사항보다 중요하지는 않지만 두 언어를 정상적으로 지원하는 코드를 작성할 수 있다면 바람직합니다.

  5. 나는 당신이 원하는 경우,이 개발자가 / (실수로) 자신의 인스턴스의 인스턴스를 안 할 수없는 진정한 싱글로 지적 다른 사람과 동의 unavailable에 관한 규정 init하고 new신중한이다.


0

스레드 안전 싱글 톤을 만들려면 다음과 같이하십시오 :

@interface SomeManager : NSObject
+ (id)sharedManager;
@end

/* thread safe */
@implementation SomeManager

static id sharedManager = nil;

+ (void)initialize {
    if (self == [SomeManager class]) {
        sharedManager = [[self alloc] init];
    }
}

+ (id)sharedManager {
    return sharedManager;
}
@end

이 블로그 는 objc / cocoa의 싱글 톤을 잘 설명합니다.


OP가 가장 현대적인 구현에 대한 특성을 요구하는 동안 매우 오래된 기사에 연결합니다.
vikingosegundo'12

1
문제는 특정 구현에 관한 것입니다. 다른 구현을 게시하십시오. 그래서 당신은 질문에 대답하려고 시도조차하지 않습니다.
vikingosegundo

1
@vikingosegundo asker ask weather GCD가 Thread safe singleton을 만드는 가장 좋은 방법입니다. 내 대답은 다른 선택을하십시오.
Hancock_Xu

asker는 특정 구현이 스레드로부터 안전한지 묻습니다. 그는 옵션을 요구하지 않습니다.
vikingosegundo

0
//Create Singleton  
  +( instancetype )defaultDBManager
    {

        static dispatch_once_t onceToken = 0;
        __strong static id _sharedObject = nil;

        dispatch_once(&onceToken, ^{
            _sharedObject = [[self alloc] init];
        });

        return _sharedObject;
    }


//In it method
-(instancetype)init
{
    self = [super init];
  if(self)
     {
   //Do your custom initialization
     }
     return self;
}

0
@interface className : NSObject{
+(className*)SingleTonShare;
}

@implementation className

+(className*)SingleTonShare{

static className* sharedObj = nil;
static dispatch_once_t once = 0;
dispatch_once(&once, ^{

if (sharedObj == nil){
    sharedObj = [[className alloc] init];
}
  });
     return sharedObj;
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.