$30 off During Our Annual Pro Sale. View Details »

What is the best way of method swizzling?

What is the best way of method swizzling?

threetreeslight

January 12, 2015
Tweet

More Decks by threetreeslight

Other Decks in Technology

Transcript

  1. What is the best way of
    method swizzling?
    Akira Miki
    Repro Inc.
    iOS meetup@voyage
    Feb 14, 2015

    View Slide

  2. Who am I?

    View Slide

  3. Akira Miki
    CTO / Repro
    @ae06710

    View Slide

  4. Reproduce real user behavior

    View Slide

  5. Reproduce User behavior

    View Slide

  6. Quantitative Analytics

    View Slide

  7. Crash Report
    (with UIView Breadcrumbs and Movie)

    View Slide

  8. What’s method swizzling?

    View Slide

  9. Replacing the
    implementation of that
    method with another
    > Meta Programming

    View Slide

  10. Like that:
    #import
    #import
    @interface GeneralSwz : NSObject
    @end
    @implementation GeneralSwz
    + (void)load
    {
    Method originalMethod = class_getInstanceMethod(self, @selector(foo));
    Method swizzledMethod = class_getInstanceMethod(self, @selector(swz_foo));
    method_exchangeImplementations(originalMethod, swizzledMethod);
    }
    - (void)foo
    {
    NSLog(@"original foo method");
    }
    - (void)swz_foo
    {
    NSLog(@"swizzed foo method");
    [self swz_foo];
    }
    @end

    View Slide

  11. Calling `foo method` Flow:
    Call `foo`
    IMP `swz_foo`
    Method `foo`
    execute `swz_foo `
    struct  objc_method  
             SEL  method_name                  OBJC2_UNAVAILABLE;  
             char  *method_types            OBJC2_UNAVAILABLE;  
             IMP  method_imp                    OBJC2_UNAVAILABLE;  
    }
    Dispatch table
    Developer
    iOS

    View Slide

  12. > method_exchangeImplementations(original, swizzled);
    It’s very easy.

    But, this has a few
    problems.

    View Slide

  13. Replace timing
    D Cannot check user define callback method
    and replace this.
    D Another third-party library replacement same
    method at the same time on another thread
    @implementation GeneralSwz
    + (void)load
    {
    Method originalMethod = class_getInstanceMethod(self, @selector(foo));
    Method swizzledMethod = class_getInstanceMethod(self, @selector(swz_foo));
    method_exchangeImplementations(originalMethod, swizzledMethod);
    }
    @end

    View Slide

  14. Target scope
    D Not by super class
    D 




    @interface Bare : NSObject
    @end
    @implementation Bare
    - (void)foo {
    NSLog(@"Bare foo method");
    }
    @end
    @interface InheritedSwz : Bare
    @end
    @implementation InheritedSwz
    + (void)load {
    Method originalMethod = class_getInstanceMethod(self, @selector(foo));
    Method swizzledMethod = class_getInstanceMethod(self, @selector(swz_foo));
    method_exchangeImplementations(originalMethod, swizzledMethod);
    }
    // we got `-[Bare swz_foo]: unrecognized selector`
    - (void)swz_foo {
    NSLog(@"Inherited swz_foo method");
    [self swz_foo];
    }
    @end

    View Slide

  15. Conflict
    D Naming conflict can happen
    @implementation CategorySwz
    // swizzling foo method to swz_foo
    - (void)foo
    {
    NSLog(@"original foo method");
    }
    - (void)swz_foo
    {
    NSLog(@"swizzed foo method");
    [self swz_foo];
    }
    @end
    @implementation CategorySwz (category)
    - (void)swz_foo
    {
    // Call this only
    NSLog(@"Cateogry swz_foo method");
    }
    @end

    View Slide

  16. Assertion
    D if check about assertion for keep everything
    running smoothly, don’t work
    @implementation AssertSwz
    // Swizzle method foo to swz_foo
    - (void)foo
    {
    assert([NSStringFromSelector(_cmd) isEqualToString:@"foo"]);
    NSLog(@"original foo method");
    }
    - (void)swz_foo
    {
    NSLog(@"swizzed foo method");
    [self swz_foo];
    }
    @end

    View Slide

  17. Best swizzling behavior is…
    D Swizzle on demand timing
    D Thread safe
    D Effect to super class
    D Can’t override replacement method
    D _cmd return original method name

    View Slide

  18. Prepare
    @interface Bare : NSObject
    @end
    @implementation Bare
    - (void)foo
    {
    NSLog(@"original foo method");
    }
    @end
    @interface General : Bare
    @end
    @implementation General
    @end
    Base Class
    - (void)foo
    General Class
    Inherited Class
    - (void)foo
    Swizzle `foo` method

    View Slide

  19. Solution-1
    D Prepare new implementation as function

    D 


    D And replace with `method_setImplementation`
    void _replacement_Method(id self, SEL _cmd)
    {
    NSLog(@"swizzed foo method”);
    ((void(*)(id,SEL))__original_Method_Imp)(self, _cmd);
    }
    static IMP __original_Method_Imp;
    @implementation InheritedBare
    + (void)swizzle
    {
    Method m = class_getInstanceMethod( [self class], @selector(foo));
    __original_Method_Imp =
    method_setImplementation( m, (IMP)_replacement_Method );
    }
    @end

    View Slide

  20. Solution-2
    D Lock thread and call once

    D 


    #import

    + (void)swizzle
    {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
    OSSpinLock lock = OS_SPINLOCK_INIT;
    OSSpinLockLock(&lock);
    Method m = class_getInstanceMethod( [self class], @selector(foo));
    __original_Method_Imp =
    method_setImplementation( m, (IMP)_replacement_Method );
    OSSpinLockUnlock(&lock);
    });
    }

    View Slide

  21. #import
    #import
    static IMP __original_Method_Imp;

    void _replacement_Method(id self, SEL _cmd)
    {
    // it will pass
    assert([NSStringFromSelector(_cmd) isEqualToString:@"foo"]);
    NSLog(@"swizzed foo method");
    ((void(*)(id,SEL))__original_Method_Imp)(self, _cmd);
    }
    @implementation GoodSwz
    + (void)swizzle
    {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
    OSSpinLock lock = OS_SPINLOCK_INIT;
    OSSpinLockLock(&lock);
    Method m = class_getInstanceMethod( [self class], @selector(foo));
    __original_Method_Imp =
    method_setImplementation( m, (IMP)_replacement_Method );
    OSSpinLockUnlock(&lock);
    });
    }
    @end

    View Slide

  22. And class method
    swizzling is…
    D as hard as what we just saw

    View Slide

  23. So, you want to implement
    all these yourself?

    No, use “rabovik/RSSwizzle”!

    View Slide

  24. > These bad practices aren’t really
    a big deal if you’re writing
    standalone applications
    - http://blog.newrelic.com/2014/04/16/right-way-to-swizzle/

    View Slide

  25. Sample Code and references
    https://github.com/ae06710/swizzle-sample
    D Method Swizzling - Written by Mattt Thompson on February 17th, 2014
    D http://nshipster.com/method-swizzling/
    D The Right Way to Swizzle in Objective-C
    D http://blog.newrelic.com/2014/04/16/right-way-to-swizzle/
    D What are the Dangers of Method Swizzling in Objective C?
    D http://stackoverflow.com/questions/5339276/what-are-the-dangers-of-method-swizzling-in-
    objective-c/8636521#8636521
    D rabovik/RSSwizzle
    D https://github.com/rabovik/RSSwizzle
    D http://www.opensource.apple.com/source/xnu/xnu-1456.1.26/libkern/libkern/OSAtomic.h
    D Objective-C Runtime Reference
    D https://developer.apple.com/library/mac/documentation/Cocoa/Reference/ObjCRuntimeRef/
    index.html#//apple_ref/c/func/method_getImplementation

    View Slide

  26. [Experts wanted]
    || ( && )

    View Slide

  27. Thank you :)

    View Slide