Objective-C でsingleton pattern

Objective-Cではマルチスレッドでも安全にsingleton objectを初期化するための dispatch_once() というのが使えるようだけど、double-checked lockingは多少効果があるようだ。

ただ最近の gcc/clangはデフォルトで -fthreadsafe-statics が有効なので Objective C++ だと dispatch_once() すら使うことなく安全に初期化できるはずで、無用なhackかもしれない。要検証。

#import <ctime>
#import <Foundation/Foundation.h>

// simple ver.
NSString* getInstance1() {
    static dispatch_once_t guard;
    static NSString* instance = nil;

    dispatch_once(&guard, ^{
        instance = @"foo";
    });

    return instance;
}

// double-checked locking ver.
NSString* getInstance2() {
    static dispatch_once_t guard;
    static NSString* instance = nil;

    if (! instance) {
        dispatch_once(&guard, ^{
            instance = @"foo";
        });
    }

    return instance;
}

int main() {
    {
        auto t0 = std::clock();
        NSString* s = nil;

        for (auto i = 0; i < 1000000000; ++i) {
            s = getInstance1();
        }

        NSLog(@"1 (%p) elapsed: %lu", s, std::clock() - t0);
    }

    {
        auto t0 = std::clock();
        NSString* s = nil;

        for (auto i = 0; i < 1000000000; ++i) {
            s = getInstance2();
        }

        NSLog(@"2 (%p) elapsed: %lu", s, std::clock() - t0);
    }

    return 0;
}

結果:

$ clang++ -framework Foundation -std=c++0x dispatch_once.mm
$ ./a.out
2012-08-15 09:17:54.689 a.out[69021:707] 1 (0x107074060) elapsed: 4328046
2012-08-15 09:17:57.832 a.out[69021:707] 2 (0x107074060) elapsed: 3140014