No edit summary |
mNo edit summary |
||
Line 1: | Line 1: | ||
The traditional singleton creation | The traditional singleton creation | ||
<source lang="objc"> | <source lang="objc"> | ||
+( | + (XXXClass *)sharedInstance { | ||
static | static XXXClass* XXXClass_sharedInst = nil; | ||
@synchronized(self) { | @synchronized(self) { | ||
if ( | if (XXXClass_sharedInst == nil) { | ||
XXXInitialize(&XXXClass_sharedInst); | |||
} | } | ||
} | } | ||
Line 13: | Line 13: | ||
is extremely inefficient because <tt>@synchronized</tt> not only lock a mutex, but also insert an exception handler (try/catch block). If you don't care about exceptions and willing to use C functions, [http://www.opengroup.org/onlinepubs/007908799/xsh/pthread_once.html <tt>pthread_once()</tt>] is a better alternative: | is extremely inefficient because <tt>@synchronized</tt> not only lock a mutex, but also insert an exception handler (try/catch block). If you don't care about exceptions and willing to use C functions, [http://www.opengroup.org/onlinepubs/007908799/xsh/pthread_once.html <tt>pthread_once()</tt>] is a better alternative: | ||
<source lang="objc"> | <source lang="objc"> | ||
static | static XXXClass *XXXClass_sharedInst = nil; | ||
static pthread_once_t | static pthread_once_t XXXClass_onceControl = PTHREAD_ONCE_INIT; | ||
static void | static void XXXInitializeOnce(void) { XXXInitialize(&XXXClass_sharedInst); } | ||
... | ... | ||
+( | + (XXXClass *)sharedInstance { | ||
pthread_once(& | pthread_once(&XXXClass_onceControl, &XXXInitializeOnce); | ||
return | return XXXClass_sharedInst; | ||
} | } | ||
</source> | </source> | ||
Line 26: | Line 26: | ||
If it is safe to create multiple copies of the singleton and destroy the extra ones, you may even use [http://developer.apple.com/mac/library/documentation/Darwin/Reference/ManPages/man3/atomic.3.html CAS]: | If it is safe to create multiple copies of the singleton and destroy the extra ones, you may even use [http://developer.apple.com/mac/library/documentation/Darwin/Reference/ManPages/man3/atomic.3.html CAS]: | ||
<source lang="objc"> | <source lang="objc"> | ||
+( | + (XXXClass *)sharedInstance { | ||
static | static XXXClass* XXXClass_sharedInst = nil; | ||
if ( | if (XXXClass_sharedInst == nil) { | ||
XXXClass* tmp; | |||
XXXInitialize(&tmp); | |||
if (!OSAtomicCompareAndSwapPtrBarrier(nil, tmp, (void*volatile*)& | if (!OSAtomicCompareAndSwapPtrBarrier(nil, tmp, (void*volatile*)&XXXClass_sharedInst))) | ||
XXXDestroy(tmp); | |||
} | } | ||
return | return XXXClass_sharedInst; | ||
} | } | ||
</source> | </source> |
Revision as of 00:15, 9 October 2013
The traditional singleton creation
+ (XXXClass *)sharedInstance {
static XXXClass* XXXClass_sharedInst = nil;
@synchronized(self) {
if (XXXClass_sharedInst == nil) {
XXXInitialize(&XXXClass_sharedInst);
}
}
return __sharedInst;
}
is extremely inefficient because @synchronized not only lock a mutex, but also insert an exception handler (try/catch block). If you don't care about exceptions and willing to use C functions, pthread_once() is a better alternative:
static XXXClass *XXXClass_sharedInst = nil;
static pthread_once_t XXXClass_onceControl = PTHREAD_ONCE_INIT;
static void XXXInitializeOnce(void) { XXXInitialize(&XXXClass_sharedInst); }
...
+ (XXXClass *)sharedInstance {
pthread_once(&XXXClass_onceControl, &XXXInitializeOnce);
return XXXClass_sharedInst;
}
pthread_once() is implemented using a spin lock (which leads to a syscall_thread_switch() kernel call in the worst case).
If it is safe to create multiple copies of the singleton and destroy the extra ones, you may even use CAS:
+ (XXXClass *)sharedInstance {
static XXXClass* XXXClass_sharedInst = nil;
if (XXXClass_sharedInst == nil) {
XXXClass* tmp;
XXXInitialize(&tmp);
if (!OSAtomicCompareAndSwapPtrBarrier(nil, tmp, (void*volatile*)&XXXClass_sharedInst)))
XXXDestroy(tmp);
}
return XXXClass_sharedInst;
}
Unfortunately, OSAtomicCompareAndSwapPtrBarrier() on ARMv4 is implemented using a global spin lock, so in principle this is nowhere faster than pthread_once(), and is even more error-prone. On ARMv6 it is implemented as a LDREX/STREX pair[1]:
ENTRY_POINT(_OSAtomicCompareAndSwapPtrBarrier)
1: ldrex r3, [r2] // load existing value and tag memory
teq r3, r0 // is it the same as oldValue?
movne r0, #0 // if not, return 0 immediately
bxne lr
strex r3, r1, [r2] // otherwise, try to store new value
cmp r3, #0 // check if the store succeeded
bne 1b // if not, try again
mov r0, #1 // return true
bx lr