Upgrade to Pro — share decks privately, control downloads, hide ads and more …

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 [email protected] Feb 14, 2015
  2. Who am I?

  3. Akira Miki CTO / Repro @ae06710

  4. Reproduce real user behavior

  5. Reproduce User behavior

  6. Quantitative Analytics

  7. Crash Report (with UIView Breadcrumbs and Movie)

  8. What’s method swizzling?

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

    Programming
  10. Like that: #import <Foundation/Foundation.h> #import <objc/runtime.h> @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
  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
  12. > method_exchangeImplementations(original, swizzled); It’s very easy.
 But, this has a

    few problems.
  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
  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
  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
  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
  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
  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
  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
  20. Solution-2 D Lock thread and call once
 D 
 


    #import <libkern/OSAtomic.h> 
 + (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); }); }
  21. #import <objc/runtime.h> #import <libkern/OSAtomic.h> 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
  22. And class method swizzling is… D as hard as what

    we just saw
  23. So, you want to implement all these yourself? 
 No,

    use “rabovik/RSSwizzle”!
  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/
  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
  26. [Experts wanted] || ( && )

  27. Thank you :)