15
15
#import " RACSubject.h"
16
16
#import " RACTuple.h"
17
17
#import " NSObject+RACDescription.h"
18
+ #import " EXTScope.h"
18
19
#import < objc/message.h>
19
20
#import < objc/runtime.h>
20
21
21
22
NSString * const RACSelectorSignalErrorDomain = @" RACSelectorSignalErrorDomain" ;
22
23
const NSInteger RACSelectorSignalErrorMethodSwizzlingRace = 1 ;
23
24
24
- static NSString * const RACSignalForSelectorAliasPrefix = @" rac_alias_" ;
25
- static NSString * const RACSignalForSelectorAliasOfOriginalPrefix = @" rac_runtime_" ;
26
25
static NSString * const RACSubclassSuffix = @" _RACSelectorSignal" ;
26
+ static const char * RACSignalForSelectorAliasPrefix = " rac_alias_" ;
27
+ static const int RACSignalForSelectorAliasPrefixLength = 10 ;
27
28
static void *RACSubclassAssociationKey = &RACSubclassAssociationKey;
29
+ static void *RACInteropImplsKey = &RACInteropImplsKey;
28
30
29
31
static NSMutableSet *swizzledClasses () {
30
32
static NSMutableSet *set;
39
41
40
42
@implementation NSObject (RACSelectorSignal)
41
43
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
+
44
47
SEL forwardInvocationSEL = @selector (forwardInvocation: );
45
48
46
49
// Set up a new version of -forwardInvocation:.
@@ -53,81 +56,80 @@ static void RACSwizzleForwardInvocation(Class class) {
53
56
// was no existing implementation, throw an unrecognized selector
54
57
// exception.
55
58
id newForwardInvocation = ^(id self, NSInvocation *invocation) {
56
- BOOL forward = NO ;
57
-
58
59
SEL originalSelector = invocation.selector ;
59
60
SEL aliasSelector = RACAliasForSelector (originalSelector);
60
- SEL aliasOfOriginalSelector = RACAliasOfOriginalForSelector (originalSelector);
61
61
62
62
RACSubject* subject = objc_getAssociatedObject (self, aliasSelector);
63
63
64
- Class baseClass = object_getClass (self);
64
+ @onExit {
65
+ if (subject != nil ) {
66
+ [subject sendNext: invocation.rac_argumentsTuple];
67
+ }
68
+ };
65
69
66
70
// RAC exchanges implementations at runtime so as to invoke the desired
67
71
// version without using fragile dependencies like libffi. This means
68
72
// all instances that had been applied `signalForSelector:` are
69
73
// non-threadsafe as any mutable instances.
70
74
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 ) {
72
80
// `self` uses a runtime subclass generated by third-party APIs, and RAC
73
81
// found an existing implementation for the selector at the setup time.
74
82
// 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
+ }
77
103
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));
94
104
invocation.selector = aliasSelector;
95
105
[invocation invoke ];
96
- } else {
97
- forward = YES ;
106
+
107
+ return ;
98
108
}
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 ;
107
111
}
108
112
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) {
110
120
// Forward the invocation to the closest `forwardInvocation:` in the
111
121
// 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);
123
125
}
124
126
};
125
127
126
- class_replaceMethod (class , forwardInvocationSEL, imp_implementationWithBlock (newForwardInvocation), " v@:@" );
128
+ class_replaceMethod (baseClass , forwardInvocationSEL, imp_implementationWithBlock (newForwardInvocation), " v@:@" );
127
129
}
128
130
129
- static void RACSwizzleRespondsToSelector (Class class ) {
130
- Class superclass = class_getSuperclass (class );
131
+ static void RACSwizzleRespondsToSelector (Class baseClass ) {
132
+ Class statedClass = class_getSuperclass (baseClass );
131
133
SEL respondsToSelectorSEL = @selector (respondsToSelector: );
132
134
133
135
// Set up a new version of -respondsToSelector: that returns YES for methods
@@ -138,24 +140,19 @@ static void RACSwizzleRespondsToSelector(Class class) {
138
140
// the instance has a signal for the selector.
139
141
// Otherwise, call the original -respondsToSelector:.
140
142
id newRespondsToSelector = ^ BOOL (id self, SEL selector) {
141
- Method method = rac_getImmediateInstanceMethod (class , selector);
143
+ Method method = rac_getImmediateInstanceMethod (baseClass , selector);
142
144
143
145
if (method != NULL && method_getImplementation (method) == _objc_msgForward) {
144
146
SEL aliasSelector = RACAliasForSelector (selector);
145
147
if (objc_getAssociatedObject (self, aliasSelector) != nil ) return YES ;
146
148
}
147
149
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);
156
153
};
157
154
158
- class_replaceMethod (class , respondsToSelectorSEL, imp_implementationWithBlock (newRespondsToSelector), " v@::" );
155
+ class_replaceMethod (baseClass , respondsToSelectorSEL, imp_implementationWithBlock (newRespondsToSelector), " v@::" );
159
156
}
160
157
161
158
static void RACSwizzleGetClass (Class class, Class statedClass) {
@@ -167,32 +164,30 @@ static void RACSwizzleGetClass(Class class, Class statedClass) {
167
164
class_replaceMethod (class, selector, newIMP, method_getTypeEncoding (method));
168
165
}
169
166
170
- static void RACSwizzleMethodSignatureForSelector (Class class) {
167
+ static void RACSwizzleMethodSignatureForSelector (Class baseClass) {
168
+ Class statedClass = class_getSuperclass (baseClass);
169
+ SEL methodSignatureForSelectorSEL = @selector (methodSignatureForSelector: );
170
+
171
171
IMP newIMP = imp_implementationWithBlock (^(id self, SEL selector) {
172
172
// Don't send the -class message to the receiver because we've changed
173
173
// 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
+
176
176
if (method == NULL ) {
177
177
// Messages that the original class dynamically implements fall
178
178
// here.
179
179
//
180
180
// 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);
187
184
}
188
185
189
186
char const *encoding = method_getTypeEncoding (method);
190
187
return [NSMethodSignature signatureWithObjCTypes: encoding];
191
188
});
192
189
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, " @@::" );
196
191
}
197
192
198
193
// It's hard to tell which struct return types use _objc_msgForward, and
@@ -266,12 +261,20 @@ static void RACCheckTypeEncoding(const char *typeEncoding) {
266
261
267
262
RACCheckTypeEncoding (typeEncoding);
268
263
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
+ }
275
278
}
276
279
277
280
// Redefine the selector to call -forwardInvocation:.
@@ -282,14 +285,21 @@ static void RACCheckTypeEncoding(const char *typeEncoding) {
282
285
}
283
286
}
284
287
285
- static SEL RACAliasOfOriginalForSelector (SEL originalSelector) {
286
- NSString *selectorName = NSStringFromSelector (originalSelector);
287
- return NSSelectorFromString ([RACSignalForSelectorAliasOfOriginalPrefix stringByAppendingString: selectorName]);
288
- }
289
-
290
288
static 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);
293
303
}
294
304
295
305
static const char *RACSignatureForUndefinedSelector (SEL selector) {
@@ -304,6 +314,20 @@ static SEL RACAliasForSelector(SEL originalSelector) {
304
314
return signature.UTF8String ;
305
315
}
306
316
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
+
307
331
static Class RACSwizzleClass (NSObject *self) {
308
332
Class statedClass = self.class ;
309
333
Class baseClass = object_getClass (self);
@@ -330,7 +354,7 @@ static Class RACSwizzleClass(NSObject *self) {
330
354
// implementation may be ignorant of methods added to this class.
331
355
@synchronized (swizzledClasses ()) {
332
356
if (![swizzledClasses () containsObject: className]) {
333
- RACSwizzleForwardInvocation (baseClass);
357
+ RACSwizzleForwardInvocation (baseClass, RACSetupInteropImplDictionary (baseClass) );
334
358
RACSwizzleRespondsToSelector (baseClass);
335
359
RACSwizzleGetClass (baseClass, statedClass);
336
360
RACSwizzleGetClass (object_getClass (baseClass), statedClass);
@@ -349,7 +373,7 @@ static Class RACSwizzleClass(NSObject *self) {
349
373
subclass = objc_allocateClassPair (baseClass, subclassName, 0 );
350
374
if (subclass == nil ) return nil ;
351
375
352
- RACSwizzleForwardInvocation (subclass);
376
+ RACSwizzleForwardInvocation (subclass, RACSetupInteropImplDictionary (subclass) );
353
377
RACSwizzleRespondsToSelector (subclass);
354
378
355
379
RACSwizzleGetClass (subclass, statedClass);
0 commit comments