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 meetup@voyage Feb 14, 2015
  2. 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
  3. 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
  4. 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
  5. 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
  6. 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
  7. 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
  8. 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
  9. 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
  10. 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
  11. 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); }); }
  12. #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
  13. > 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/
  14. 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