2013-01-31 33 views
2

我正在经历和更换@synthesized(个体)锁定重量/该方法使用objc_msgSendSuper调用一个类方法

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保持“已经初始化”的路径非常快。这是我唯一关心的。

#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,但我试图合理地滥用运行时:)

+4

不要这样做。只是...不。 –

+0

那么恕我直言,没有人应该重写allocWithZone,但我们不能总是得到我们想要的。我有兴趣听到除了担心objective-c运行时(这是你的朋友)之外,为什么这是无效的原因。 – Steazy

+0

因为这是无用的代码,无缘无故地与运行时混淆,而不是一个很好的理由。如果你告诉我们为什么你认为你需要这样做,也许我们可以建议一个更好的方法? –

回答

7

好的,这并不是真的那么棘手,一旦我回忆起元类包含所有通过 - [self class]或+ [self] - >获得的类实例的方法信息谢谢http://www.cocoawithlove.com/2010/01/what-is-meta-class-in-objective-c.html

发生此错误是因为我要求运行时在NSObject的一组实例方法中查找方法,这显然不包含allocWithZone:。错误日志中的错误大概是因为接收者是一个元类实例而产生的,而且Apple有他们的实习生实现错误日志。

因此,当通过objc_msgSendSuper调用一个常规的实例方法时,您会传递一个元类实例作为objc_super.super_class,以调用一个类方法,需要元类本身(所有内容都是一级的)。

实施例,以及图,帮助我明白这 - (http://www.sealiesoftware.com/blog/archive/2009/04/14/objc_explain_Classes_and_metaclasses.html

struct objc_super mySuper; 
mySuper.receiver = theClassToInit; //this is our receiver, no doubt about it 
//either grab the super class and get its metaclass 
mySuper.super_class = object_getClass(class_getSuperclass(theClassToInit)); 
//or grab the metaclass, and get its super class, this is the exact same object 
mySuper.super_class = class_getSuperclass(object_getClass(theClassToInit)); 

然后,消息可被正确地解析。现在我开始注意了:P

无论如何,现在我发现我的错误,我觉得我已经调整了我的Objc运行时的理解。我还修复了两年前由一个我从未见过的架构错误,这个错误不需要修改和重新测试3个项目和2个静态库(上帝我爱Objective-C)中的几十个类。用一个简单的函数调用替换@synchronized结构也会减半这些方法的编译代码大小。作为奖励,我们所有的单例访问器现在(更多)都是线程安全的,因为这样做的性能成本现在可以忽略不计。那些天真地重新获取单例对象多次(或循环中)的方法现在已经看到了一个巨大的加速,因为他们不必每次调用都需要多次获取和释放一个互斥量。总而言之,我非常高兴,一切都如我所愿。

我对NSObject的类做了一个“正常”的Objective-C方法,它可以用于实例和Class对象,从而允许您在外部调用超类的消息实现。警告:这只是为了好玩,或单元测试,或混合方法,或者可能是一个非常酷的游戏。

@implementation NSObject (Convenience) 

-(id)performSelector:(SEL)selector asClass:(Class)class 
{ 
    struct objc_super mySuper = { 
     .receiver = self, 
     .super_class = class_isMetaClass(object_getClass(self)) //check if we are an instance or Class 
         ? object_getClass(class)    //if we are a Class, we need to send our metaclass (our Class's Class) 
         : class         //if we are an instance, we need to send our Class (which we already have) 
    }; 

    id (*objc_superAllocTyped)(struct objc_super *, SEL) = (void *)&objc_msgSendSuper; //cast our pointer so the compiler can sort out the ABI 
    return (*objc_superAllocTyped)(&mySuper, selector); 
} 

所以

[self performSelector:@selector(dealloc) asClass:[self superclass]]; 

将相当于

[super dealloc]; 

进行运行探险家!不要让反对者将你拖入他们手中和黑魔法盒的地方,很难在那里做出毫不妥协的真棒节目*。

*请尽情享受Objective-C运行时。请咨询您的QA团队,了解超过四个小时的任何错误。

+0

我正在寻找这样的解决方案好几天:)!完全同意你关于这种主题的反对者:p感谢分享和上帝保佑objc ^^ – polo987