Commit aa8ba8c8 authored by Justin Spahr-Summers's avatar Justin Spahr-Summers
Browse files

Merge pull request #1058 from ReactiveCocoa/signal-selector-class

Signal selector class
parents e63f9d59 b822f885
Showing with 72 additions and 0 deletions
+72 -0
......@@ -116,6 +116,42 @@ static void RACSwizzleRespondsToSelector(Class class) {
class_replaceMethod(class, respondsToSelectorSEL, imp_implementationWithBlock(newRespondsToSelector), method_getTypeEncoding(respondsToSelectorMethod));
}
static void RACSwizzleGetClass(Class class, Class statedClass) {
SEL selector = @selector(class);
Method method = class_getInstanceMethod(class, selector);
IMP newIMP = imp_implementationWithBlock(^(id self) {
return statedClass;
});
class_replaceMethod(class, selector, newIMP, method_getTypeEncoding(method));
}
static void RACSwizzleMethodSignatureForSelector(Class class) {
SEL selector = @selector(methodSignatureForSelector:);
Method methodSignatureForSelectorMethod = class_getInstanceMethod(class, selector);
IMP newIMP = imp_implementationWithBlock(^(id self, SEL selector) {
// Don't send the -class message to the receiver because we've changed
// that to return the original class.
Class actualClass = object_getClass(self);
Method method = class_getInstanceMethod(actualClass, selector);
if (method == NULL) {
// Messages that the original class dynamically implements fall
// here.
//
// Call the original class' -methodSignatureForSelector:.
struct objc_super target = {
.super_class = class_getSuperclass(actualClass),
.receiver = self,
};
NSMethodSignature * (*messageSend)(struct objc_super *, SEL) = (__typeof__(messageSend))objc_msgSendSuper;
return messageSend(&target, @selector(methodSignatureForSelector:));
}
char const *encoding = method_getTypeEncoding(method);
return [NSMethodSignature signatureWithObjCTypes:encoding];
});
class_replaceMethod(class, selector, newIMP, method_getTypeEncoding(methodSignatureForSelectorMethod));
}
// It's hard to tell which struct return types use _objc_msgForward, and
// which use _objc_msgForward_stret instead, so just exclude all struct, array,
// union, complex and vector return types.
......@@ -237,6 +273,9 @@ static Class RACSwizzleClass(NSObject *self) {
if (![swizzledClasses() containsObject:className]) {
RACSwizzleForwardInvocation(baseClass);
RACSwizzleRespondsToSelector(baseClass);
RACSwizzleGetClass(baseClass, statedClass);
RACSwizzleGetClass(object_getClass(baseClass), statedClass);
RACSwizzleMethodSignatureForSelector(baseClass);
[swizzledClasses() addObject:className];
}
}
......@@ -253,6 +292,12 @@ static Class RACSwizzleClass(NSObject *self) {
RACSwizzleForwardInvocation(subclass);
RACSwizzleRespondsToSelector(subclass);
RACSwizzleGetClass(subclass, statedClass);
RACSwizzleGetClass(object_getClass(subclass), statedClass);
RACSwizzleMethodSignatureForSelector(subclass);
objc_registerClassPair(subclass);
}
......
......@@ -384,4 +384,31 @@ describe(@"-rac_signalForSelector:fromProtocol", ^{
});
});
describe(@"class reporting", ^{
__block RACTestObject *object;
__block Class originalClass;
beforeEach(^{
object = [[RACTestObject alloc] init];
originalClass = object.class;
});
it(@"should report the original class", ^{
[object rac_signalForSelector:@selector(lifeIsGood:)];
expect(object.class).to.beIdenticalTo(originalClass);
});
it(@"should report the original class when it's KVO'd after dynamically subclassing", ^{
[object rac_signalForSelector:@selector(lifeIsGood:)];
RACObserve(object, objectValue);
expect(object.class).to.beIdenticalTo(originalClass);
});
it(@"should report the original class when it's KVO'd before dynamically subclassing", ^{
RACObserve(object, objectValue);
[object rac_signalForSelector:@selector(lifeIsGood:)];
expect(object.class).to.beIdenticalTo(originalClass);
});
});
SpecEnd
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment