我正在经历并用这种方法替换@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);
}
虽然有点冗长,但在无争议的情况下表现出明显更好的性能
连同这个小宏(请原谅格式不好,它很简单)。为了允许在初始 nil 检查之后声明块,看起来可以帮助 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,但我试图合理地滥用运行时:)