1515#import " RACSubject.h"
1616#import " RACTuple.h"
1717#import " NSObject+RACDescription.h"
18+ #import " EXTScope.h"
1819#import < objc/message.h>
1920#import < objc/runtime.h>
2021
2122NSString * const RACSelectorSignalErrorDomain = @" RACSelectorSignalErrorDomain" ;
2223const NSInteger RACSelectorSignalErrorMethodSwizzlingRace = 1 ;
2324
24- static NSString * const RACSignalForSelectorAliasPrefix = @" rac_alias_" ;
25- static NSString * const RACSignalForSelectorAliasOfOriginalPrefix = @" rac_runtime_" ;
2625static NSString * const RACSubclassSuffix = @" _RACSelectorSignal" ;
26+ static const char * RACSignalForSelectorAliasPrefix = " rac_alias_" ;
27+ static const int RACSignalForSelectorAliasPrefixLength = 10 ;
2728static void *RACSubclassAssociationKey = &RACSubclassAssociationKey;
29+ static void *RACInteropImplsKey = &RACInteropImplsKey;
2830
2931static NSMutableSet *swizzledClasses () {
3032 static NSMutableSet *set;
3941
4042@implementation NSObject (RACSelectorSignal)
4143
42- static void RACSwizzleForwardInvocation (Class class) {
43- Class superclass = class_getSuperclass (class);
44+ static void RACSwizzleForwardInvocation (Class baseClass, NSDictionary *interopImpls) {
45+ Class statedClass = class_getSuperclass (baseClass);
46+
4447 SEL forwardInvocationSEL = @selector (forwardInvocation: );
4548
4649 // Set up a new version of -forwardInvocation:.
@@ -53,81 +56,80 @@ static void RACSwizzleForwardInvocation(Class class) {
5356 // was no existing implementation, throw an unrecognized selector
5457 // exception.
5558 id newForwardInvocation = ^(id self, NSInvocation *invocation) {
56- BOOL forward = NO ;
57-
5859 SEL originalSelector = invocation.selector ;
5960 SEL aliasSelector = RACAliasForSelector (originalSelector);
60- SEL aliasOfOriginalSelector = RACAliasOfOriginalForSelector (originalSelector);
6161
6262 RACSubject* subject = objc_getAssociatedObject (self, aliasSelector);
6363
64- Class baseClass = object_getClass (self);
64+ @onExit {
65+ if (subject != nil ) {
66+ [subject sendNext: invocation.rac_argumentsTuple];
67+ }
68+ };
6569
6670 // RAC exchanges implementations at runtime so as to invoke the desired
6771 // version without using fragile dependencies like libffi. This means
6872 // all instances that had been applied `signalForSelector:` are
6973 // non-threadsafe as any mutable instances.
7074
71- if ([baseClass instancesRespondToSelector: aliasOfOriginalSelector]) {
75+ Method method = class_getInstanceMethod (statedClass, originalSelector);
76+ const char * typeEncoding = method_getTypeEncoding (method);
77+
78+ IMP interopImpl = [[interopImpls objectForKey: [NSValue valueWithPointer: originalSelector]] pointerValue ];
79+ if (interopImpl != nil ) {
7280 // `self` uses a runtime subclass generated by third-party APIs, and RAC
7381 // found an existing implementation for the selector at the setup time.
7482 // Call that implementation if it is not the ObjC message forwarder.
75- Method xchgMethod = class_getInstanceMethod (baseClass, aliasOfOriginalSelector);
76- IMP impl = method_getImplementation (xchgMethod);
83+ IMP oldImpl = class_replaceMethod (baseClass, originalSelector, interopImpl, typeEncoding);
84+ invocation.selector = originalSelector;
85+ [invocation invoke ];
86+ class_replaceMethod (baseClass, originalSelector, oldImpl, typeEncoding);
87+ return ;
88+ }
89+
90+ BOOL forwardRegardless = NO ;
91+
92+ if (method != nil ) {
93+ IMP statedClassImpl = method_getImplementation (method);
94+
95+ if (statedClassImpl != _objc_msgForward) {
96+ // The stated class has an implementation of the selector. Call that
97+ // implementation if it is not the ObjC message forwarder.
98+ Method aliasMethod = rac_getImmediateInstanceMethod (baseClass, aliasSelector);
99+
100+ if (statedClassImpl != method_getImplementation (aliasMethod)) {
101+ class_replaceMethod (baseClass, aliasSelector, statedClassImpl, typeEncoding);
102+ }
77103
78- if (impl != _objc_msgForward) {
79- IMP oldImpl = class_replaceMethod (baseClass, originalSelector, impl, method_getTypeEncoding (xchgMethod));
80- invocation.selector = originalSelector;
81- [invocation invoke ];
82- class_replaceMethod (baseClass, originalSelector, oldImpl, method_getTypeEncoding (xchgMethod));
83- } else {
84- forward = YES ;
85- }
86- } else if ([superclass instancesRespondToSelector: originalSelector]) {
87- // The stated class has an implementation of the selector. Call that
88- // implementation if it is not the ObjC message forwarder.
89- Method method = class_getInstanceMethod (superclass, originalSelector);
90- IMP impl = method_getImplementation (method);
91-
92- if (impl != _objc_msgForward) {
93- class_replaceMethod (baseClass, aliasSelector, impl, method_getTypeEncoding (method));
94104 invocation.selector = aliasSelector;
95105 [invocation invoke ];
96- } else {
97- forward = YES ;
106+
107+ return ;
98108 }
99- } else {
100- // No appropriate implementation was found. Forward the invocation to the
101- // stated class' `forwardInvocation:` only if the selector had not been
102- // intercepted via `signalForSelector:`.
103- //
104- // `subject` is usually null except for optional protocol requirements
105- // that are implemented at runtime through RAC.
106- forward = subject == nil ;
109+
110+ forwardRegardless = YES ;
107111 }
108112
109- if (forward) {
113+ // No appropriate implementation was found. Forward the invocation to the
114+ // stated class' `forwardInvocation:` only if the selector had not been
115+ // intercepted via `signalForSelector:`.
116+ //
117+ // `subject` is usually null except for optional protocol requirements
118+ // that are implemented at runtime through RAC.
119+ if (subject == nil || forwardRegardless) {
110120 // Forward the invocation to the closest `forwardInvocation:` in the
111121 // inheritance hierarchy.
112- struct objc_super target = {
113- .super_class = superclass,
114- .receiver = self,
115- };
116-
117- void *(*superForwardInvocation)(struct objc_super *, SEL , NSInvocation *) = (__typeof__ (superForwardInvocation)) objc_msgSendSuper;
118- superForwardInvocation (&target, forwardInvocationSEL, invocation);
119- }
120-
121- if (subject != nil ) {
122- [subject sendNext: invocation.rac_argumentsTuple];
122+ Method forwardInvocationMethod = class_getInstanceMethod (statedClass, forwardInvocationSEL);
123+ void *(*superForwardInvocation)(id , SEL , NSInvocation *) = (__typeof__ (superForwardInvocation)) method_getImplementation (forwardInvocationMethod);
124+ superForwardInvocation (self, forwardInvocationSEL, invocation);
123125 }
124126 };
125127
126- class_replaceMethod (class , forwardInvocationSEL, imp_implementationWithBlock (newForwardInvocation), " v@:@" );
128+ class_replaceMethod (baseClass , forwardInvocationSEL, imp_implementationWithBlock (newForwardInvocation), " v@:@" );
127129}
128130
129- static void RACSwizzleRespondsToSelector (Class class ) {
130- Class superclass = class_getSuperclass (class );
131+ static void RACSwizzleRespondsToSelector (Class baseClass ) {
132+ Class statedClass = class_getSuperclass (baseClass );
131133 SEL respondsToSelectorSEL = @selector (respondsToSelector: );
132134
133135 // Set up a new version of -respondsToSelector: that returns YES for methods
@@ -138,24 +140,19 @@ static void RACSwizzleRespondsToSelector(Class class) {
138140 // the instance has a signal for the selector.
139141 // Otherwise, call the original -respondsToSelector:.
140142 id newRespondsToSelector = ^ BOOL (id self, SEL selector) {
141- Method method = rac_getImmediateInstanceMethod (class , selector);
143+ Method method = rac_getImmediateInstanceMethod (baseClass , selector);
142144
143145 if (method != NULL && method_getImplementation (method) == _objc_msgForward) {
144146 SEL aliasSelector = RACAliasForSelector (selector);
145147 if (objc_getAssociatedObject (self, aliasSelector) != nil ) return YES ;
146148 }
147149
148- struct objc_super target = {
149- .super_class = superclass,
150- .receiver = self,
151- };
152-
153- BOOL (*superRespondsToSelector)(struct objc_super *, SEL , SEL ) = (__typeof__ (superRespondsToSelector)) objc_msgSendSuper;
154-
155- return superRespondsToSelector (&target, respondsToSelectorSEL, selector);
150+ Method superMethod = class_getInstanceMethod (statedClass, respondsToSelectorSEL);
151+ BOOL (*superRespondsToSelector)(id , SEL , SEL ) = (__typeof__ (superRespondsToSelector)) method_getImplementation (superMethod);
152+ return superRespondsToSelector (self, respondsToSelectorSEL, selector);
156153 };
157154
158- class_replaceMethod (class , respondsToSelectorSEL, imp_implementationWithBlock (newRespondsToSelector), " v@::" );
155+ class_replaceMethod (baseClass , respondsToSelectorSEL, imp_implementationWithBlock (newRespondsToSelector), " v@::" );
159156}
160157
161158static void RACSwizzleGetClass (Class class, Class statedClass) {
@@ -167,32 +164,30 @@ static void RACSwizzleGetClass(Class class, Class statedClass) {
167164 class_replaceMethod (class, selector, newIMP, method_getTypeEncoding (method));
168165}
169166
170- static void RACSwizzleMethodSignatureForSelector (Class class) {
167+ static void RACSwizzleMethodSignatureForSelector (Class baseClass) {
168+ Class statedClass = class_getSuperclass (baseClass);
169+ SEL methodSignatureForSelectorSEL = @selector (methodSignatureForSelector: );
170+
171171 IMP newIMP = imp_implementationWithBlock (^(id self, SEL selector) {
172172 // Don't send the -class message to the receiver because we've changed
173173 // that to return the original class.
174- Class actualClass = object_getClass (self );
175- Method method = class_getInstanceMethod (actualClass, selector);
174+ Method method = class_getInstanceMethod (baseClass, selector );
175+
176176 if (method == NULL ) {
177177 // Messages that the original class dynamically implements fall
178178 // here.
179179 //
180180 // Call the original class' -methodSignatureForSelector:.
181- struct objc_super target = {
182- .super_class = class_getSuperclass (class),
183- .receiver = self,
184- };
185- NSMethodSignature * (*messageSend)(struct objc_super *, SEL , SEL ) = (__typeof__ (messageSend))objc_msgSendSuper;
186- return messageSend (&target, @selector (methodSignatureForSelector: ), selector);
181+ Method superMethod = class_getInstanceMethod (statedClass, methodSignatureForSelectorSEL);
182+ NSMethodSignature * (*messageSend)(id , SEL , SEL ) = (__typeof__ (messageSend)) method_getImplementation (superMethod);
183+ return messageSend (self, methodSignatureForSelectorSEL, selector);
187184 }
188185
189186 char const *encoding = method_getTypeEncoding (method);
190187 return [NSMethodSignature signatureWithObjCTypes: encoding];
191188 });
192189
193- SEL selector = @selector (methodSignatureForSelector: );
194- Method methodSignatureForSelectorMethod = class_getInstanceMethod (class, selector);
195- class_replaceMethod (class, selector, newIMP, method_getTypeEncoding (methodSignatureForSelectorMethod));
190+ class_replaceMethod (baseClass, methodSignatureForSelectorSEL, newIMP, " @@::" );
196191}
197192
198193// It's hard to tell which struct return types use _objc_msgForward, and
@@ -266,12 +261,20 @@ static void RACCheckTypeEncoding(const char *typeEncoding) {
266261
267262 RACCheckTypeEncoding (typeEncoding);
268263
269- Method existingMethod = rac_getImmediateInstanceMethod (class, selector);
270-
271- if (existingMethod) {
272- SEL sel = RACAliasOfOriginalForSelector (selector);
273- BOOL addedAlias __attribute__ ((unused)) = class_addMethod (class, sel, method_getImplementation (existingMethod), typeEncoding);
274- NSCAssert (addedAlias, @" Existing external implementation for %@ has already been copied to %@ on %@ " , NSStringFromSelector (selector), NSStringFromSelector(sel), class);
264+ Method dynamicImmediateMethod = rac_getImmediateInstanceMethod (class, selector);
265+ if (dynamicImmediateMethod) {
266+ IMP dynamicImmediateImpl = method_getImplementation (dynamicImmediateMethod);
267+ if (dynamicImmediateImpl != _objc_msgForward) {
268+ @synchronized (class) {
269+ NSMutableDictionary * interopImpls = objc_getAssociatedObject (class, RACInteropImplsKey);
270+ NSValue * key = [NSValue valueWithPointer: selector];
271+
272+ if ([interopImpls objectForKey: key] == nil ) {
273+ [interopImpls setObject: [NSValue valueWithPointer: dynamicImmediateImpl]
274+ forKey: key];
275+ }
276+ }
277+ }
275278 }
276279
277280 // Redefine the selector to call -forwardInvocation:.
@@ -282,14 +285,21 @@ static void RACCheckTypeEncoding(const char *typeEncoding) {
282285 }
283286}
284287
285- static SEL RACAliasOfOriginalForSelector (SEL originalSelector) {
286- NSString *selectorName = NSStringFromSelector (originalSelector);
287- return NSSelectorFromString ([RACSignalForSelectorAliasOfOriginalPrefix stringByAppendingString: selectorName]);
288- }
289-
290288static SEL RACAliasForSelector (SEL originalSelector) {
291- NSString *selectorName = NSStringFromSelector (originalSelector);
292- return NSSelectorFromString ([RACSignalForSelectorAliasPrefix stringByAppendingString: selectorName]);
289+ const char * selectorString = sel_getName (originalSelector);
290+ unsigned long length = strlen (selectorString);
291+ unsigned long prefixedLength = length + RACSignalForSelectorAliasPrefixLength;
292+
293+ char * buffer = malloc (length + RACSignalForSelectorAliasPrefixLength + 1 );
294+ @onExit {
295+ free (buffer);
296+ };
297+
298+ memcpy (buffer, RACSignalForSelectorAliasPrefix, RACSignalForSelectorAliasPrefixLength);
299+ memcpy (buffer + RACSignalForSelectorAliasPrefixLength, selectorString, length);
300+ *(buffer + prefixedLength) = ' \0 ' ;
301+
302+ return sel_registerName (buffer);
293303}
294304
295305static const char *RACSignatureForUndefinedSelector (SEL selector) {
@@ -304,6 +314,20 @@ static SEL RACAliasForSelector(SEL originalSelector) {
304314 return signature.UTF8String ;
305315}
306316
317+ static NSDictionary * RACSetupInteropImplDictionary (Class baseClass) {
318+ NSDictionary * interopImpls;
319+
320+ @synchronized (baseClass) {
321+ interopImpls = objc_getAssociatedObject (baseClass, RACInteropImplsKey);
322+ if (interopImpls == nil ) {
323+ interopImpls = [[NSMutableDictionary alloc ] init ];
324+ objc_setAssociatedObject (baseClass, RACInteropImplsKey, interopImpls, OBJC_ASSOCIATION_RETAIN_NONATOMIC );
325+ }
326+ }
327+
328+ return interopImpls;
329+ }
330+
307331static Class RACSwizzleClass (NSObject *self) {
308332 Class statedClass = self.class ;
309333 Class baseClass = object_getClass (self);
@@ -330,7 +354,7 @@ static Class RACSwizzleClass(NSObject *self) {
330354 // implementation may be ignorant of methods added to this class.
331355 @synchronized (swizzledClasses ()) {
332356 if (![swizzledClasses () containsObject: className]) {
333- RACSwizzleForwardInvocation (baseClass);
357+ RACSwizzleForwardInvocation (baseClass, RACSetupInteropImplDictionary (baseClass) );
334358 RACSwizzleRespondsToSelector (baseClass);
335359 RACSwizzleGetClass (baseClass, statedClass);
336360 RACSwizzleGetClass (object_getClass (baseClass), statedClass);
@@ -349,7 +373,7 @@ static Class RACSwizzleClass(NSObject *self) {
349373 subclass = objc_allocateClassPair (baseClass, subclassName, 0 );
350374 if (subclass == nil ) return nil ;
351375
352- RACSwizzleForwardInvocation (subclass);
376+ RACSwizzleForwardInvocation (subclass, RACSetupInteropImplDictionary (subclass) );
353377 RACSwizzleRespondsToSelector (subclass);
354378
355379 RACSwizzleGetClass (subclass, statedClass);
0 commit comments