Skip to content

Instantly share code, notes, and snippets.

@rudyjahchan
Last active August 13, 2016 08:29
  • Star 14 You must be signed in to star a gist
  • Fork 4 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save rudyjahchan/2191796 to your computer and use it in GitHub Desktop.
Monkey-Patching iOS with Objective-C Categories Part III: Swizzling
#import <Foundation/Foundation.h>
@interface NSObject (Swizzle)
+ (void)swizzleInstanceSelector:(SEL)originalSelector
withNewSelector:(SEL)newSelector;
@end
#import "NSObject+Swizzle.h"
#import <objc/runtime.h>
@implementation NSObject (Swizzle)
+ (void) swizzleInstanceSelector:(SEL)originalSelector
withNewSelector:(SEL)newSelector
{
Method originalMethod = class_getInstanceMethod(self, originalSelector);
Method newMethod = class_getInstanceMethod(self, newSelector);
BOOL methodAdded = class_addMethod([self class],
originalSelector,
method_getImplementation(newMethod),
method_getTypeEncoding(newMethod));
if (methodAdded) {
class_replaceMethod([self class],
newSelector,
method_getImplementation(originalMethod),
method_getTypeEncoding(originalMethod));
} else {
method_exchangeImplementations(originalMethod, newMethod);
}
}
@end
SEL firstMethodSelector = @selector(firstMethod);
SEL secondMethodSelector = @selector(secondMethod);
Method firstMethod = class_getInstanceMethod(self, firstMethodSelector);
Method secondMethod = class_getInstanceMethod(self, secondMethodSelector);
BOOL methodAdded = class_addMethod([self class],
firstMethodSelector,
method_getImplementation(secondMethod),
method_getTypeEncoding(secondMethod));
if (methodAdded) {
class_replaceMethod([self class],
secondMethodSelector,
method_getImplementation(firstMethod),
method_getTypeEncoding(firstMethod));
} else {
method_exchangeImplementations(firstMethod, secondMethod);
}
@implementation UIViewController (TourGuide)
+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
SEL viewWillAppearSelector = @selector(viewWillAppear:);
SEL tourGuideWillAppearSelector = @selector(tourGuideWillAppear:);
Method originalMethod = class_getInstanceMethod(self, viewWillAppearSelector);
Method newMethod = class_getInstanceMethod(self, tourGuideWillAppearSelector);
BOOL methodAdded = class_addMethod([self class],
viewWillAppearSelector,
method_getImplementation(newMethod),
method_getTypeEncoding(newMethod));
if (methodAdded) {
class_replaceMethod([self class],
tourGuideWillAppearSelector,
method_getImplementation(originalMethod),
method_getTypeEncoding(originalMethod));
} else {
method_exchangeImplementations(originalMethod, newMethod);
}
});
}
#import <UIKit/UIKit.h>
@interface UIViewController (TourGuide)
@property (nonatomic, retain) NSArray* tourSteps;
@end
#import "UIViewController_TourGuide.h"
#import "NSObject_Swizzle.h"
static char tourStepsKey;
@implementation UIViewController (TourGuide)
+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
[self swizzleInstanceSelector:@selector(viewWillAppear:)
withNewSelector:@selector(tourGuideWillAppear:)];
});
}
- (NSArray*) getTourSteps {
return objc_getAssociatedObject(self, &tourStepsKey);
}
- (void) setTourSteps:(NSArray*)tourSteps {
objc_setAssociatedObject(self, &tourStepsKey,
tourSteps, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (void) displayTour {
// run the tour, and save the fact it was run
}
- (BOOL) isTourRan {
// return TRUE if tour has been run
}
- (void) tourGuideWillAppear:(BOOL)animated {
// call the old implementation, now under the new message signature
[self tourGuideWillAppear:animated];
if (![self isTourRan]) {
[self displayTour];
}
}
@end
#import "UIViewController_TourGuide.h"
@implementation UIViewController (TourGuide)
//...
- (void) tourGuideWillAppear:(BOOL)animated {
// call the old implementation, now under the new message signature
[self tourGuideWillAppear:animated];
if (![self isTourRan]) {
[self displayTour];
}
}
//...
@end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment