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

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



답변

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


답변

인스턴스 유형

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


답변

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


답변

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


답변

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


답변

[[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 호출의 결과가 아니지만 괜찮습니다.


답변

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

몇 가지 생각 :

  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신중한이다.