【问题标题】:Thread-safety of method swizzling方法混合的线程安全
【发布时间】:2013-12-16 15:19:38
【问题描述】:

方法 swizzling 用于以下单例初始化程序,但我不确定 swizzling 的线程安全性。

当一个线程即将调用一个即将被另一个方法调动的方法时会发生什么? 在有线程要调用该方法的情况下,随时进行调配是否安全?请用官方参考回答。谢谢

#import <dispatch/dispatch.h>
#import <objc/runtime.h>

@implementation MySingleton

static MySingleton * __singleton = nil;

+ (MySingleton *) sharedInstance_accessor
{
    return ( __singleton );
}

+ (MySingleton *) sharedInstance
{
    static dispatch_once_t __once = 0;

    dispatch_once( &__once, ^{ 
                __singleton = [[self alloc] init]; 
        Method plainMethod = class_getInstanceMethod( self, @selector(sharedInstance) );
        Method accessorMethod = class_getInstanceMethod( self, @selector(sharedInstance_accessor) );
        method_exchangeImplementations( plainMethod, accessorMethod );
        });
    return ( __singleton );
}

@end

https://gist.github.com/MSch/943369

【问题讨论】:

  • 这段代码有什么意义? dispatch_once 几乎没有开销。事实上,我敢打赌,检查令牌十几个(可能更多)次比进行这种混杂要快。代码也不太复杂。
  • “请用官方参考资料回答”听起来您希望有人为您搜索文档...
  • 我发布了一个答案,但我必须说我不明白你想要完成什么,也不明白你为什么需要 swizzling 来做到这一点。对此的解释将有助于澄清您的问题。
  • 请不要使用此代码,即使它有效且“安全”;这太糟糕了。坚持包含 SharedInstance 模式。

标签: objective-c multithreading thread-safety swizzling


【解决方案1】:

根据documentationmethod_exchangeImplementations() 是原子的。大概这意味着如果在运行method_exchangeImplementations() 的同时从另一个线程调用正在交换的两种方法之一,它可能会得到以前的(非交换的)方法,但不会得到不一致的东西(例如不会崩溃)。

注意dispatch_once()是线程安全的,也意味着只要通过+sharedInstance获得对单例实例的所有引用,在swizzling完成之前没有线程会引用该对象。

最后,我通常不喜欢在答案中添加这种东西,因为我意识到有时提问者只是想更多地了解事物的工作原理,而不是尝试编写生产代码。但是,如果您打算在实际的运输代码中使用它,您应该重新考虑它。 Swizzling 往往是一种“糟糕的代码味道”,很可能有更好的方法来完成您想要完成的任何事情(我仍然不清楚)。

【讨论】:

  • @BradAllred,是的,我最初在我的回答中注意到了这一点,但他的代码点不够清楚,我不确定它是否适用。我将进行编辑以将其添加回来,但需要注意。
【解决方案2】:

我怀疑这方面是否有官方参考,但不需要。

首先,该方法的可执行代码不是从内存中取消映射的。因此,安装哪个实现并不重要,之前的实现仍然可以执行。

但是,这确实意味着您必须确保您的数据管理是线程安全的(假设没有人尝试直接调用 sharedInstance_accessor)。

这就留下了一个问题,即method_exchangeImplementations() 是否是线程安全的。 The source 说它是(并且它不是之前的几个主要版本),文档也是如此。

正如 Brad Allred 所说,这不太可能是值得追求的优化。

【讨论】:

  • method_exchangeImplementations() 在文档中是原子的,但它不是我想要的。它是并发方法混合所必需的,这在此自定义类中不会出现。我正在寻找的是方法调用和方法调配之间的线程安全。我认为 runtimeLock(opensource.apple.com/source/objc4/objc4-551.1/runtime/…) 实现了这一点。
  • objc_msgSend(opensource.apple.com/source/objc4/objc4-551.1/runtime/…) 调用同样使用 runtimeLock 的 lookupMethodAndLoadCache3。 setImplementation 也使用此同步 obj(runtimeLock),因此 setImplementation 本身与方法调用一起是线程安全的。
  • 我强烈建议不要直接使用运行时的锁。您一定会发现无数种死锁方式。
猜你喜欢
  • 2012-03-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-11-06
  • 1970-01-01
  • 2013-03-31
相关资源
最近更新 更多