Skip to content

Commit

Permalink
added inter-class swizzling, fixed bug in class method swizzling and …
Browse files Browse the repository at this point in the history
…added tests for swizzling inherited methods in subclass.
  • Loading branch information
petejkim committed Dec 1, 2010
1 parent 9c28c42 commit 505fcd4
Show file tree
Hide file tree
Showing 6 changed files with 130 additions and 31 deletions.
6 changes: 4 additions & 2 deletions ConciseKit.h
Expand Up @@ -13,8 +13,10 @@
+ (NSString *)documentPath;
+ (NSString *)appPath;

+ (BOOL)swizzleMethod:(SEL)originalSelector with:(SEL)anotherSelector inClass:(Class)klass;
+ (BOOL)swizzleClassMethod:(SEL)originalSelector with:(SEL)anotherSelector inClass:(Class)klass;
+ (BOOL)swizzleMethod:(SEL)originalSelector with:(SEL)anotherSelector in:(Class)klass;
+ (BOOL)swizzleMethod:(SEL)originalSelector in:(Class)klass with:(SEL)anotherSelector in:(Class)anotherKlass;
+ (BOOL)swizzleClassMethod:(SEL)originalSelector with:(SEL)anotherSelector in:(Class)klass;
+ (BOOL)swizzleClassMethod:(SEL)originalSelector in:(Class)klass with:(SEL)anotherSelector in:(Class)anotherKlass;

@end

Expand Down
31 changes: 20 additions & 11 deletions ConciseKit.m
Expand Up @@ -23,38 +23,47 @@ + (NSString *)appPath {
return [[NSBundle mainBundle] bundlePath];
}

+ (BOOL)swizzleMethod:(SEL)originalSelector with:(SEL)anotherSelector inClass:(Class)klass {
+ (BOOL)swizzleMethod:(SEL)originalSelector with:(SEL)anotherSelector in:(Class)klass {
return [self swizzleMethod:originalSelector in:klass with:anotherSelector in:klass];
}

+ (BOOL)swizzleMethod:(SEL)originalSelector in:(Class)klass with:(SEL)anotherSelector in:(Class)anotherKlass {
Method originalMethod = class_getInstanceMethod(klass, originalSelector);
Method anotherMethod = class_getInstanceMethod(klass, anotherSelector);
Method anotherMethod = class_getInstanceMethod(anotherKlass, anotherSelector);
if(!originalMethod || !anotherMethod) {
return NO;
}
IMP originalMethodImplementation = class_getMethodImplementation(klass, originalSelector);
IMP anotherMethodImplementation = class_getMethodImplementation(klass, anotherSelector);
IMP anotherMethodImplementation = class_getMethodImplementation(anotherKlass, anotherSelector);
if(class_addMethod(klass, originalSelector, originalMethodImplementation, method_getTypeEncoding(originalMethod))) {
originalMethod = class_getInstanceMethod(klass, originalSelector);
}
if(class_addMethod(klass, anotherSelector, anotherMethodImplementation, method_getTypeEncoding(anotherMethod))) {
anotherMethod = class_getInstanceMethod(klass, anotherSelector);
if(class_addMethod(anotherKlass, anotherSelector, anotherMethodImplementation, method_getTypeEncoding(anotherMethod))) {
anotherMethod = class_getInstanceMethod(anotherKlass, anotherSelector);
}
method_exchangeImplementations(originalMethod, anotherMethod);
return YES;
}

+ (BOOL)swizzleClassMethod:(SEL)originalSelector with:(SEL)anotherSelector inClass:(Class)klass {
+ (BOOL)swizzleClassMethod:(SEL)originalSelector with:(SEL)anotherSelector in:(Class)klass {
return [self swizzleClassMethod:originalSelector in:klass with:anotherSelector in:klass];
}

+ (BOOL)swizzleClassMethod:(SEL)originalSelector in:(Class)klass with:(SEL)anotherSelector in:(Class)anotherKlass {
Method originalMethod = class_getClassMethod(klass, originalSelector);
Method anotherMethod = class_getClassMethod(klass, anotherSelector);
Method anotherMethod = class_getClassMethod(anotherKlass, anotherSelector);
if(!originalMethod || !anotherMethod) {
return NO;
}
IMP originalMethodImplementation = class_getMethodImplementation(klass, originalSelector);
IMP anotherMethodImplementation = class_getMethodImplementation(klass, anotherSelector);
Class metaClass = objc_getMetaClass(class_getName(klass));
Class anotherMetaClass = objc_getMetaClass(class_getName(anotherKlass));
IMP originalMethodImplementation = class_getMethodImplementation(metaClass, originalSelector);
IMP anotherMethodImplementation = class_getMethodImplementation(anotherMetaClass, anotherSelector);
if(class_addMethod(metaClass, originalSelector, originalMethodImplementation, method_getTypeEncoding(originalMethod))) {
originalMethod = class_getClassMethod(klass, originalSelector);
}
if(class_addMethod(metaClass, anotherSelector, anotherMethodImplementation, method_getTypeEncoding(anotherMethod))) {
anotherMethod = class_getClassMethod(klass, anotherSelector);
if(class_addMethod(anotherMetaClass, anotherSelector, anotherMethodImplementation, method_getTypeEncoding(anotherMethod))) {
anotherMethod = class_getClassMethod(anotherKlass, anotherSelector);
}
method_exchangeImplementations(originalMethod, anotherMethod);
return YES;
Expand Down
97 changes: 82 additions & 15 deletions ConciseKitSpecs/Spec/ConciseKitSpec.m
Expand Up @@ -5,6 +5,7 @@
#import "SpecHelper.h"
#import "ConciseKit.h"
#import "Foo.h"
#import <objc/objc-class.h>

DESCRIBE($) {
describe(@"path", ^{
Expand Down Expand Up @@ -34,32 +35,98 @@
});

describe(@"method swizzling", ^{
describe(@"+swizzleMethod:with:inClass:", ^{
it(@"swizzles instances methods", ^{
Foo *obj = [[Foo alloc] init];
assertThat([obj foo], equalTo(@"-foo"));
assertThat([obj bar], equalTo(@"-bar"));
[$ swizzleMethod:@selector(foo) with:@selector(bar) inClass:[Foo class]];
assertThat([obj foo], equalTo(@"-bar"));
assertThat([obj bar], equalTo(@"-foo"));
[$ swizzleMethod:@selector(foo) with:@selector(bar) inClass:[Foo class]];
assertThat([obj foo], equalTo(@"-foo"));
assertThat([obj bar], equalTo(@"-bar"));
[obj release];
describe(@"+swizzleMethod:with:in:", ^{
it(@"swizzles instance methods", ^{
Foo *foo = [[Foo alloc] init];
assertThat([foo foo], equalTo(@"-foo"));
assertThat([foo bar], equalTo(@"-bar"));
[$ swizzleMethod:@selector(foo) with:@selector(bar) in:[Foo class]];
assertThat([foo foo], equalTo(@"-bar"));
assertThat([foo bar], equalTo(@"-foo"));
[$ swizzleMethod:@selector(foo) with:@selector(bar) in:[Foo class]];
assertThat([foo foo], equalTo(@"-foo"));
assertThat([foo bar], equalTo(@"-bar"));
[foo release];
});

context(@"swizzling inherited methods", ^{
it(@"adds the inherited methods to the subclass and then swizzles, preserving the original method in superclass", ^{
Foo *foo = [[Foo alloc] init];
SubFoo *subFoo = [[SubFoo alloc] init];
assertThat([foo foo], equalTo(@"-foo"));
assertThat([subFoo foo], equalTo(@"-foo"));
assertThat([subFoo bar], equalTo(@"-SubFoo::bar"));
[$ swizzleMethod:@selector(foo) with:@selector(bar) in:[SubFoo class]];
assertThat([foo foo], equalTo(@"-foo"));
assertThat([subFoo foo], equalTo(@"-SubFoo::bar"));
assertThat([subFoo bar], equalTo(@"-foo"));
[$ swizzleMethod:@selector(foo) with:@selector(bar) in:[SubFoo class]];
assertThat([foo foo], equalTo(@"-foo"));
assertThat([subFoo foo], equalTo(@"-foo"));
assertThat([subFoo bar], equalTo(@"-SubFoo::bar"));
[foo release];
[subFoo release];
});
});
});

describe(@"+swizzleClassMethod:with:inClass:", ^{
describe(@"+swizzleMethod:in:with:in:", ^{
it(@"swizzles instance methods", ^{
Foo *foo = [[Foo alloc] init];
Bar *bar = [[Bar alloc] init];
assertThat([foo foo], equalTo(@"-foo"));
assertThat([bar bar], equalTo(@"-Bar::bar"));
[$ swizzleMethod:@selector(foo) in:[Foo class] with:@selector(bar) in:[Bar class]];
assertThat([foo foo], equalTo(@"-Bar::bar"));
assertThat([bar bar], equalTo(@"-foo"));
[$ swizzleMethod:@selector(foo) in:[Foo class] with:@selector(bar) in:[Bar class]];
assertThat([foo foo], equalTo(@"-foo"));
assertThat([bar bar], equalTo(@"-Bar::bar"));
[foo release];
[bar release];
});
});

describe(@"+swizzleClassMethod:with:in:", ^{
it(@"swizzles class methods", ^{
assertThat([Foo foo], equalTo(@"+foo"));
assertThat([Foo bar], equalTo(@"+bar"));
[$ swizzleClassMethod:@selector(foo) with:@selector(bar) inClass:[Foo class]];
[$ swizzleClassMethod:@selector(foo) with:@selector(bar) in:[Foo class]];
assertThat([Foo foo], equalTo(@"+bar"));
assertThat([Foo bar], equalTo(@"+foo"));
[$ swizzleClassMethod:@selector(foo) with:@selector(bar) inClass:[Foo class]];
[$ swizzleClassMethod:@selector(foo) with:@selector(bar) in:[Foo class]];
assertThat([Foo foo], equalTo(@"+foo"));
assertThat([Foo bar], equalTo(@"+bar"));
});

context(@"swizzling inherited class methods", ^{
it(@"adds the inherited class methods to the subclass and then swizzles, preserving the original method in superclass", ^{
assertThat([Foo foo], equalTo(@"+foo"));
assertThat([SubFoo foo], equalTo(@"+foo"));
assertThat([SubFoo bar], equalTo(@"+SubFoo::bar"));
[$ swizzleClassMethod:@selector(foo) with:@selector(bar) in:[SubFoo class]];
assertThat([Foo foo], equalTo(@"+foo"));
assertThat([SubFoo foo], equalTo(@"+SubFoo::bar"));
assertThat([SubFoo bar], equalTo(@"+foo"));
[$ swizzleClassMethod:@selector(foo) with:@selector(bar) in:[SubFoo class]];
assertThat([Foo foo], equalTo(@"+foo"));
assertThat([SubFoo foo], equalTo(@"+foo"));
assertThat([SubFoo bar], equalTo(@"+SubFoo::bar"));
});
});
});

describe(@"+swizzleClassMethod:in:with:in:", ^{
it(@"swizzles class methods", ^{
assertThat([Foo foo], equalTo(@"+foo"));
assertThat([Bar bar], equalTo(@"+Bar::bar"));
[$ swizzleClassMethod:@selector(foo) in:[Foo class] with:@selector(bar) in:[Bar class]];
assertThat([Foo foo], equalTo(@"+Bar::bar"));
assertThat([Bar bar], equalTo(@"+foo"));
[$ swizzleClassMethod:@selector(foo) in:[Foo class] with:@selector(bar) in:[Bar class]];
assertThat([Foo foo], equalTo(@"+foo"));
assertThat([Bar bar], equalTo(@"+Bar::bar"));
});
});
});
}
Expand Down
8 changes: 8 additions & 0 deletions ConciseKitSpecs/Spec/Fixtures/Foo.h
Expand Up @@ -9,4 +9,12 @@
+ (NSString *)bar;
- (NSString *)foo;
- (NSString *)bar;
@end

@interface SubFoo : Foo {}
@end

@interface Bar : NSObject {}
+ (NSString *)bar;
- (NSString *)bar;
@end
10 changes: 10 additions & 0 deletions ConciseKitSpecs/Spec/Fixtures/Foo.m
Expand Up @@ -9,4 +9,14 @@ + (NSString *)foo { return @"+foo"; }
+ (NSString *)bar { return @"+bar"; }
- (NSString *)foo { return @"-foo"; }
- (NSString *)bar { return @"-bar"; }
@end

@implementation SubFoo
+ (NSString *)bar { return @"+SubFoo::bar"; }
- (NSString *)bar { return @"-SubFoo::bar"; }
@end

@implementation Bar
+ (NSString *)bar { return @"+Bar::bar"; }
- (NSString *)bar { return @"-Bar::bar"; }
@end
9 changes: 6 additions & 3 deletions README.markdown
Expand Up @@ -6,15 +6,18 @@ A set of Objective-C additions and macros that lets you to write code more quick

### Method Swizzling

[$ swizzleMethod:@selector(foo) with:@selector(bar) inClass:[Foo class]];
[$ swizzleClassMethod:@selector(foo) with:@selector(bar) inClass:[Foo class]];
[$ swizzleMethod:@selector(foo) with:@selector(bar) in:[Foo class]];
[$ swizzleMethod:@selector(foo) in:[Foo class] with:@selector(bar) in:[Bar class]];

[$ swizzleClassMethod:@selector(foo) with:@selector(bar) in:[Foo class]];
[$ swizzleClassMethod:@selector(foo) in:[Foo class] with:@selector(bar) in:[Bar class]];

### Path

[$ homePath]; => path to user's home directory
[$ desktopPath]; => path to user's desktop directory
[$ documentPath]; => path to user's document directory
[$ appPath]; => ptah to app directory
[$ appPath]; => path to app directory

## Macros

Expand Down

0 comments on commit 505fcd4

Please sign in to comment.