1

Method swizzling is used in the following singleton initializer but i am not sure about thread safety of swizzling.

What happens while a thread is about to call a method which is about to be swizzled by another method? Is it safe to swizzle in any time, while there are threads about to call that method? Answer with official references please. Thanks

#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

lockedscope
  • 965
  • 17
  • 45
  • 1
    what is the point of this code? dispatch_once carries virtually no overhead. In fact ill bet its faster to check the token a dozen (probably way more) times than it is to do this swizzling. code is less complicated too. – Brad Allred Nov 30 '13 at 16:21
  • 2
    "Answer with official references please" sounds like you want somebody to search the docs for you... – Dave Nov 30 '13 at 16:22
  • 1
    I posted an answer, but I must say that I don't understand what you're trying to accomplish, nor why you need swizzling to do it. An explanation of that would help clear up your question. – Andrew Madsen Nov 30 '13 at 16:23
  • 2
    please don't use this code even if it works and is "safe"; its horrible. stick with the contained SharedInstance pattern. – Brad Allred Nov 30 '13 at 16:25

2 Answers2

2

I doubt there is an official reference on this, but none is needed.

First, the executable code of the method is not un-mapped from memory. Thus, it doesn't matter which implementation is installed, the prior implementation is still available for execution.

That does mean, however, that you have to ensure that your data management is thread safe (which it is, assuming no one tries to call sharedInstance_accessor directly).

So that leaves a question as to whether method_exchangeImplementations() is thread safe. The source says it is (and it was not several major release prior) and so does the documentation.

As Brad Allred said, this is unlikely to be an optimization worth pursuing.

bbum
  • 162,346
  • 23
  • 271
  • 359
  • 1
    method_exchangeImplementations() is atomic by docs but its not what i am looking for. it is required for concurrent method swizzles which will not expected in this custom class. what i am looking for is thread-safety between a method call and a method swizzle. i think runtimeLock(http://www.opensource.apple.com/source/objc4/objc4-551.1/runtime/objc-runtime-new.mm) achieves this. – lockedscope Nov 30 '13 at 20:53
  • objc_msgSend(http://www.opensource.apple.com/source/objc4/objc4-551.1/runtime/Messengers.subproj/objc-msg-x86_64.s) calls lookupMethodAndLoadCache3 which also uses runtimeLock. setImplementation also uses this sync obj(runtimeLock) thus setImplementation is itself thread safe alongside a method call. – lockedscope Nov 30 '13 at 21:19
  • I would recommend strongly against mucking with the runtime's locks directly. You're sure to discover a myriad of ways of deadlocking. – bbum Nov 30 '13 at 23:39
1

According to the documentation, method_exchangeImplementations() is atomic. Presumably that means that if one of the two methods being exchanged is called from another thread at the same time as method_exchangeImplementations() is being run, it may get the previous (non-swapped) method, but it won't get something inconsistent (e.g. won't crash).

Note that dispatch_once() is thread safe, and also means that as long as all references to the singleton instance are obtained via +sharedInstance, no thread will have a reference to the object before the swizzling is complete.

Finally, I don't normally like adding this kind of thing to answers, because I realize that sometimes questioners are just trying to learn more about how things work, rather than trying to write production code. However, if you're planning to use this in real, shipping code, you should rethink it. Swizzling tends to be a "bad code smell", and it's likely that there's a better way to do whatever it is you're trying to accomplish (which is still not clear to me).

Community
  • 1
  • 1
Andrew Madsen
  • 21,309
  • 5
  • 56
  • 97
  • @BradAllred, yes, I originally noted that in my answer, but the point of his code is unclear enough that I wasn't sure if it applied. I'll edit to add it back with a caveat. – Andrew Madsen Nov 30 '13 at 16:24