LoginSignup
57
57

More than 5 years have passed since last update.

Objective-C で継承可能な Singleton Class を作る

Last updated at Posted at 2013-04-05

Objective-C でシングルトンクラスを作るのはよくやることかと思います。よくやる手段としては、以下のような方法ですね。

Singleton.m
@implementation Singleton

+ (Singleton*)sharedInstance {
    static Singleton* _instance;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        _instance = [[Singleton alloc]
                           initSharedInstance];
    });
    return _instance;
}

- (id)initSharedInstance {
    self = [super init];
    if (self) {
        // do something
    }
    return self;
}

- (id)init {
    [self doesNotRecognizeSelector:_cmd]; // init を直接呼ぼうとしたらエラーを発生させる
    return nil;
}
@end

dispatch_once は、このブロック内の処理はアプリケーションのライフサイクルの中で一度しか呼ばれないというものです。
大抵の場合これで間に合うのですが、継承したクラスを作ろうとした場合にはうまく動きません。たとえば

SingletonA.h
@interface SingletonA : Singleton
-(void)printA;
@end
SingletonB.h
@interface SingletonB : Singleton
-(void)printB;
@end

というクラスを作った場合、以下のコードは、エラーになってしまいます。

SingletonA *s1 = [SingletonA sharedInstance];
[s1 printA]; // 'NSInvalidArgumentException', reason: '-[Singleton printA]: unrecognized selector sent to instance が発生

[SingletonA sharedInstance] が、SingletonA ではなくSingleton クラスのインスタンスを返してしまうからですね。
また、複数のクラスがこの Singleton クラスを継承する可能性があるため、継承元のオブジェクト側でも、それぞれのクラスインスタンスを保持できるようにしなくてはいけません。以下のようにすることで実装ができました。

Singleton.m
@implementation Singleton
static NSMutableDictionary *_instances;

+ (id) sharedInstance {
    __block Singleton *obj;
    @synchronized(self) {
        if ([_instances objectForKey:NSStringFromClass(self)] == nil) {
            obj = [[self alloc] initSharedInstance];
        }
    }
    obj = [_instances objectForKey:NSStringFromClass(self)];
    return obj;
}

+ (id)allocWithZone:(NSZone *)zone {
    @synchronized(self) {
        if ([_instances objectForKey:NSStringFromClass(self)] == nil) {
            id instance = [super allocWithZone:zone];
            if (_instances == nil) {
                _instances = [[NSMutableDictionary alloc] initWithCapacity:0];
            }
            [_instances setObject:instance forKey:NSStringFromClass(self)];
            return instance;
        }
    }
    return nil;
}
- (id)initSharedInstance {
    self = [super init];
    if (self) {
        // do something
    }
    return self;
}

- (id)init {
    [self doesNotRecognizeSelector:_cmd]; // init を直接呼ぼうとしたらエラーを発生させる
    return nil;
}


- (void)print{
    NSLog(@"hoge");
}

@end

static な NSDictionary 、 _instances にクラス名をキーにしてインスタンスを保存しておき、それを返しているわけですね。

これで、以下の様なコードも問題なく動作します。

SingletonA *s1 = [SingletonA sharedInstance];
[s1 printA];
SingletonB *s2 = [SingletonB sharedInstance];
[s2 printB];
57
57
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
57
57