Home

Awesome

RSSwizzle

Safe method swizzling done right.

Motivation

Classical method swizzling with method_exchangeImplementations is quite simple, but it has a lot of limitations:

For more details see discussions in: 1, 2, 3, 4, 5.

RSSwizzle avoids all these known pitfalls.

Usage

Original implementation must always be called from the new implementation. And because of the the fact that for safe and robust swizzling original implementation must be dynamically fetched at the time of calling and not at the time of swizzling <sup>(1,2)</sup>, swizzling API is slightly unusual.

Example for swizzling -(int)calculate:(int)number; method:

RSSwizzleInstanceMethod(classToSwizzle,
                        @selector(calculate:),
                        RSSWReturnType(int),
                        RSSWArguments(int number),
                        RSSWReplacement(
{
    // The following code will be used as the new implementation.

    // Calling original implementation.
    int res = RSSWCallOriginal(number);
    // Returning modified return value.
    return res + 1;
}), 0, NULL);

Alternative API

Alternatively you may use an API without macros, though it is a little bit complicated.

You should pass a factory block that returns the block for the new implementation of the swizzled method. And use swizzleInfo argument to retrieve and call original implementation.

Example for swizzling -(int)calculate:(int)number; method:

SEL selector = @selector(calculate:);
[RSSwizzle
 swizzleInstanceMethod:selector
 inClass:classToSwizzle
 newImpFactory:^id(RSSwizzleInfo *swizzleInfo) {
     // This block will be used as the new implementation.
     return ^int(__unsafe_unretained id self, int num){
         // You MUST always cast implementation to the correct function pointer.
         int (*originalIMP)(__unsafe_unretained id, SEL, int);
         originalIMP = (__typeof(originalIMP))[swizzleInfo getOriginalImplementation];
         // Calling original implementation.
         int res = originalIMP(self,selector,num);
         // Returning modified return value.
         return res + 1;
     };
 }
 mode:RSSwizzleModeAlways
 key:NULL];

Class Method Swizzling

Class method swizzling is done with a similar API.

Example for swizzling +(int)calculate:(int)number; method:

RSSwizzleClassMethod(classToSwizzle,
                     @selector(calculate:),
                     RSSWReturnType(int),
                     RSSWArguments(int number),
                     RSSWReplacement(
{
    // Calling original implementation.
    int res = RSSWCallOriginal(number);
    // Returning modified return value.
    return res + 1;
}));

Modes

Swizzling frequently goes along with checking whether this particular class (or one of its superclasses) has been already swizzled. Here the mode and key parameters can help. Possible mode values:

Here is an example of swizzling -(void)dealloc; only in case when neither class and no one of its superclasses has been already swizzled with the given key:

static const void *key = &key;
SEL selector = NSSelectorFromString(@"dealloc");
RSSwizzleInstanceMethod(classToSwizzle,
                        selector,
                        RSSWReturnType(void),
                        RSSWArguments(),
                        RSSWReplacement(
{
    NSLog(@"Deallocating %@.",self);
    RSSWCallOriginal();
}), RSSwizzleModeOncePerClassAndSuperclasses, key);

Note: RSSwizzleModeOncePerClassAndSuperclasses mode does not guarantees that your implementation will be called only once per method call. If the order of swizzling is: first inherited class, second superclass; then both swizzlings will be done and the new implementation will be called twice.

Thread safety

RSSwizzle is fully thread safe. You do not need any additional synchronization.

CocoaPods

Add RSSwizzle to your Podfile.

Requirements

Author

Yan Rabovik (@rabovik on twitter)

License

MIT License.