Я проходил и заменял блокировки @synthesized(self) этим методом
void _ThreadsafeInit(Class theClassToInit, void *volatile *theVariableItLivesIn, void(^InitBlock)(void))
{
//this is what super does :X
struct objc_super mySuper = {
.receiver = (id)theClassToInit,
.super_class = class_getSuperclass(theClassToInit)
};
id (*objc_superAllocTyped)(struct objc_super *, SEL, NSZone *) = (void *)&objc_msgSendSuper;
// id (*objc_superAllocTyped)(id objc_super, SEL, NSZone *) = (void *)&objc_msgSend;
do {
id temp = [(*objc_superAllocTyped)(&mySuper /*theClassToInit*/, @selector(allocWithZone:), NULL) init];//get superclass in case alloc is blocked in this class;
if(OSAtomicCompareAndSwapPtrBarrier(0x0, temp, theVariableItLivesIn)) { //atomic operation forces synchronization
if( InitBlock != NULL ) {
InitBlock(); //only the thread that succesfully set sharedInstance pointer gets here
}
break;
}
else
{
[temp release]; //any thread that fails to set sharedInstance needs to clean up after itself
}
} while (*theVariableItLivesIn == NULL);
}
который, хотя и немного более подробный, демонстрирует значительно лучшую производительность в неоспоримых случаях.
вместе с этим маленьким макросом (извините за плохое форматирование, это очень просто). Чтобы блок мог быть объявлен после начальной нулевой проверки, LLVM помогает LLVM очень быстро поддерживать «уже инициализированный» путь. Это единственное, о ком я забочусь.
#define ThreadsafeFastInit(theClassToInit, theVariableToStoreItIn, aVoidBlockToRunAfterInit) if( theVariableToStoreItIn == nil) { _ThreadsafeInitWithBlock(theClassToInit, (void *)&theVariableToStoreItIn, aVoidBlockToRunAfterInit); }
Поэтому изначально реализовал его, используя закомментированные разделы для objc_superAllocTyped (на самом деле сначала используя [theClassToInit allocWithZone:NULL], что было определенно лучшим подходом :)), который отлично работал, пока я не понял, что большинство синглетонов в проекте переопределяли allocWithZone для вернуть одноэлементный метод... бесконечный цикл. Поэтому я решил, что использование objc_msgSendSuper должно быстро разобраться, но я получаю эту ошибку.
[51431:17c03] +[DataUtils allocWithZone:]: unrecognized selector sent to class 0x4f9584
Ошибка, похоже, не связана с реальной проблемой, так как...
(lldb) po 0x4f9584
$1 = 5215620 DataUtils
(lldb) print (BOOL)[$1 respondsToSelector:@selector(allocWithZone:)]
(BOOL) $2 = YES
Так что я определенно что-то упустил... Я сравнил сборку, сгенерированную методом [super allocWithZone:NULL] в пустом классе... почти идентично, за исключением того, что вызываемые функции имеют разные имена (может быть, просто с использованием разных символов, понятия не имею , плохо читается).
Любые идеи? Я могу использовать class_getClassMethod в суперклассе и напрямую вызывать IMP, но я пытаюсь быть разумным в своем злоупотреблении средой выполнения :)