Giter Site home page Giter Site logo

rsswizzle's Introduction

RSSwizzle

Safe method swizzling done right.

Motivation

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

  • It is safe only if swizzling is done in the +load method. If you need to swizzle methods during application's lifetime you should take into account that third-party code may do swizzling of the same method in another thread at the same time.
  • The swizzled method must be implemented by the class itself and not by superclasses. Workarounds by copying implementation from the superclass do not really work. Original implementation in the superclass must be fetched at the time of calling, not at the time of swizzling (1,2).
  • The swizzled method implementation must not rely on the _cmd argument. (And generally you can not be sure in it (5).)
  • Naming conflicts are possible (3).

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 (1,2), 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:

  • RSSwizzleModeAlways RSSwizzle always does swizzling regardless of the given key.
  • RSSwizzleModeOncePerClass RSSwizzle does not do swizzling if the same class has been swizzled earlier with the same key.
  • RSSwizzleModeOncePerClassAndSuperclasses RSSwizzle does not do swizzling if the same class or one of its superclasses have been swizzled earlier with the same key.

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

  • iOS 5.0+
  • Mac OS X 10.7+
  • ARC

Author

Yan Rabovik (@rabovik on twitter)

License

MIT License.

rsswizzle's People

Contributors

alkalin3 avatar rabovik avatar

Watchers

 avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.