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
25
static NSString * const RACSignalForSelectorAliasPrefix = @" rac_alias_" ;
25
- static NSString * const RACSignalForSelectorAliasOfOriginalPrefix = @" rac_runtime_" ;
26
26
static NSString * const RACSubclassSuffix = @" _RACSelectorSignal" ;
27
27
static void *RACSubclassAssociationKey = &RACSubclassAssociationKey;
28
+ static void *RACInteropImplsKey = &RACInteropImplsKey;
28
29
29
30
static NSMutableSet *swizzledClasses () {
30
31
static NSMutableSet *set;
39
40
40
41
@implementation NSObject (RACSelectorSignal)
41
42
42
- static void RACSwizzleForwardInvocation (Class class) {
43
- Class superclass = class_getSuperclass (class);
43
+ static void RACSwizzleForwardInvocation (Class baseClass, NSDictionary *interopImpls) {
44
+ Class statedClass = class_getSuperclass (baseClass);
45
+
44
46
SEL forwardInvocationSEL = @selector (forwardInvocation: );
45
47
46
48
// Set up a new version of -forwardInvocation:.
@@ -53,81 +55,73 @@ static void RACSwizzleForwardInvocation(Class class) {
53
55
// was no existing implementation, throw an unrecognized selector
54
56
// exception.
55
57
id newForwardInvocation = ^(id self, NSInvocation *invocation) {
56
- BOOL forward = NO ;
57
-
58
58
SEL originalSelector = invocation.selector ;
59
59
SEL aliasSelector = RACAliasForSelector (originalSelector);
60
- SEL aliasOfOriginalSelector = RACAliasOfOriginalForSelector (originalSelector);
61
60
62
61
RACSubject* subject = objc_getAssociatedObject (self, aliasSelector);
63
62
64
- Class baseClass = object_getClass (self);
63
+ @onExit {
64
+ if (subject != nil ) {
65
+ [subject sendNext: invocation.rac_argumentsTuple];
66
+ }
67
+ };
65
68
66
69
// RAC exchanges implementations at runtime so as to invoke the desired
67
70
// version without using fragile dependencies like libffi. This means
68
71
// all instances that had been applied `signalForSelector:` are
69
72
// non-threadsafe as any mutable instances.
70
73
71
- if ([baseClass instancesRespondToSelector: aliasOfOriginalSelector]) {
74
+ Method method = class_getInstanceMethod (statedClass, originalSelector);
75
+ const char * typeEncoding = method_getTypeEncoding (method);
76
+
77
+ IMP interopImpl = [[interopImpls objectForKey: [NSValue valueWithPointer: originalSelector]] pointerValue ];
78
+ if (interopImpl != nil ) {
72
79
// `self` uses a runtime subclass generated by third-party APIs, and RAC
73
80
// found an existing implementation for the selector at the setup time.
74
81
// Call that implementation if it is not the ObjC message forwarder.
75
- Method xchgMethod = class_getInstanceMethod (baseClass, aliasOfOriginalSelector);
76
- IMP impl = method_getImplementation (xchgMethod);
77
-
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]) {
82
+ IMP oldImpl = class_replaceMethod (baseClass, originalSelector, interopImpl, typeEncoding);
83
+ invocation.selector = originalSelector;
84
+ [invocation invoke ];
85
+ class_replaceMethod (baseClass, originalSelector, oldImpl, typeEncoding);
86
+ return ;
87
+ }
88
+
89
+ IMP statedClassImpl = method_getImplementation (method);
90
+ if (statedClassImpl != _objc_msgForward) {
87
91
// The stated class has an implementation of the selector. Call that
88
92
// implementation if it is not the ObjC message forwarder.
89
- Method method = class_getInstanceMethod (superclass, originalSelector);
90
- IMP impl = method_getImplementation (method);
93
+ Method method = rac_getImmediateInstanceMethod (baseClass, aliasSelector);
91
94
92
- if (impl != _objc_msgForward) {
93
- class_replaceMethod (baseClass, aliasSelector, impl, method_getTypeEncoding (method));
94
- invocation.selector = aliasSelector;
95
- [invocation invoke ];
96
- } else {
97
- forward = YES ;
95
+ if (statedClassImpl != method_getImplementation (method)) {
96
+ class_replaceMethod (baseClass, aliasSelector, statedClassImpl, typeEncoding);
98
97
}
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 ;
107
- }
108
98
109
- if (forward) {
110
- // Forward the invocation to the closest `forwardInvocation:` in the
111
- // inheritance hierarchy.
112
- struct objc_super target = {
113
- .super_class = superclass,
114
- .receiver = self,
115
- };
99
+ invocation.selector = aliasSelector;
100
+ [invocation invoke ];
116
101
117
- void *(*superForwardInvocation)(struct objc_super *, SEL , NSInvocation *) = (__typeof__ (superForwardInvocation)) objc_msgSendSuper;
118
- superForwardInvocation (&target, forwardInvocationSEL, invocation);
102
+ return ;
119
103
}
120
104
121
- if (subject != nil ) {
122
- [subject sendNext: invocation.rac_argumentsTuple];
105
+ // No appropriate implementation was found. Forward the invocation to the
106
+ // stated class' `forwardInvocation:` only if the selector had not been
107
+ // intercepted via `signalForSelector:`.
108
+ //
109
+ // `subject` is usually null except for optional protocol requirements
110
+ // that are implemented at runtime through RAC.
111
+ if (subject == nil ) {
112
+ // Forward the invocation to the closest `forwardInvocation:` in the
113
+ // inheritance hierarchy.
114
+ Method forwardInvocationMethod = class_getInstanceMethod (statedClass, forwardInvocationSEL);
115
+ void *(*superForwardInvocation)(id , SEL , NSInvocation *) = (__typeof__ (superForwardInvocation)) method_getImplementation (forwardInvocationMethod);
116
+ superForwardInvocation (self, forwardInvocationSEL, invocation);
123
117
}
124
118
};
125
119
126
- class_replaceMethod (class , forwardInvocationSEL, imp_implementationWithBlock (newForwardInvocation), " v@:@" );
120
+ class_replaceMethod (baseClass , forwardInvocationSEL, imp_implementationWithBlock (newForwardInvocation), " v@:@" );
127
121
}
128
122
129
- static void RACSwizzleRespondsToSelector (Class class ) {
130
- Class superclass = class_getSuperclass (class );
123
+ static void RACSwizzleRespondsToSelector (Class baseClass ) {
124
+ Class statedClass = class_getSuperclass (baseClass );
131
125
SEL respondsToSelectorSEL = @selector (respondsToSelector: );
132
126
133
127
// Set up a new version of -respondsToSelector: that returns YES for methods
@@ -138,24 +132,19 @@ static void RACSwizzleRespondsToSelector(Class class) {
138
132
// the instance has a signal for the selector.
139
133
// Otherwise, call the original -respondsToSelector:.
140
134
id newRespondsToSelector = ^ BOOL (id self, SEL selector) {
141
- Method method = rac_getImmediateInstanceMethod (class , selector);
135
+ Method method = rac_getImmediateInstanceMethod (baseClass , selector);
142
136
143
137
if (method != NULL && method_getImplementation (method) == _objc_msgForward) {
144
138
SEL aliasSelector = RACAliasForSelector (selector);
145
139
if (objc_getAssociatedObject (self, aliasSelector) != nil ) return YES ;
146
140
}
147
141
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);
142
+ Method superMethod = class_getInstanceMethod (statedClass, respondsToSelectorSEL);
143
+ BOOL (*superRespondsToSelector)(id , SEL , SEL ) = (__typeof__ (superRespondsToSelector)) method_getImplementation (superMethod);
144
+ return superRespondsToSelector (self, respondsToSelectorSEL, selector);
156
145
};
157
146
158
- class_replaceMethod (class , respondsToSelectorSEL, imp_implementationWithBlock (newRespondsToSelector), " v@::" );
147
+ class_replaceMethod (baseClass , respondsToSelectorSEL, imp_implementationWithBlock (newRespondsToSelector), " v@::" );
159
148
}
160
149
161
150
static void RACSwizzleGetClass (Class class, Class statedClass) {
@@ -167,32 +156,30 @@ static void RACSwizzleGetClass(Class class, Class statedClass) {
167
156
class_replaceMethod (class, selector, newIMP, method_getTypeEncoding (method));
168
157
}
169
158
170
- static void RACSwizzleMethodSignatureForSelector (Class class) {
159
+ static void RACSwizzleMethodSignatureForSelector (Class baseClass) {
160
+ Class statedClass = class_getSuperclass (baseClass);
161
+ SEL methodSignatureForSelectorSEL = @selector (methodSignatureForSelector: );
162
+
171
163
IMP newIMP = imp_implementationWithBlock (^(id self, SEL selector) {
172
164
// Don't send the -class message to the receiver because we've changed
173
165
// that to return the original class.
174
- Class actualClass = object_getClass (self );
175
- Method method = class_getInstanceMethod (actualClass, selector);
166
+ Method method = class_getInstanceMethod (baseClass, selector );
167
+
176
168
if (method == NULL ) {
177
169
// Messages that the original class dynamically implements fall
178
170
// here.
179
171
//
180
172
// 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);
173
+ Method superMethod = class_getInstanceMethod (statedClass, methodSignatureForSelectorSEL);
174
+ NSMethodSignature * (*messageSend)(id , SEL , SEL ) = (__typeof__ (messageSend)) method_getImplementation (superMethod);
175
+ return messageSend (self, methodSignatureForSelectorSEL, selector);
187
176
}
188
177
189
178
char const *encoding = method_getTypeEncoding (method);
190
179
return [NSMethodSignature signatureWithObjCTypes: encoding];
191
180
});
192
181
193
- SEL selector = @selector (methodSignatureForSelector: );
194
- Method methodSignatureForSelectorMethod = class_getInstanceMethod (class, selector);
195
- class_replaceMethod (class, selector, newIMP, method_getTypeEncoding (methodSignatureForSelectorMethod));
182
+ class_replaceMethod (baseClass, methodSignatureForSelectorSEL, newIMP, " @@::" );
196
183
}
197
184
198
185
// It's hard to tell which struct return types use _objc_msgForward, and
@@ -266,12 +253,20 @@ static void RACCheckTypeEncoding(const char *typeEncoding) {
266
253
267
254
RACCheckTypeEncoding (typeEncoding);
268
255
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);
256
+ Method dynamicImmediateMethod = rac_getImmediateInstanceMethod (class, selector);
257
+ if (dynamicImmediateMethod) {
258
+ IMP dynamicImmediateImpl = method_getImplementation (dynamicImmediateMethod);
259
+ if (dynamicImmediateImpl != _objc_msgForward) {
260
+ @synchronized (class) {
261
+ NSMutableDictionary * interopImpls = objc_getAssociatedObject (class, RACInteropImplsKey);
262
+ NSValue * key = [NSValue valueWithPointer: selector];
263
+
264
+ if ([interopImpls objectForKey: key] == nil ) {
265
+ [interopImpls setObject: [NSValue valueWithPointer: dynamicImmediateImpl]
266
+ forKey: key];
267
+ }
268
+ }
269
+ }
275
270
}
276
271
277
272
// Redefine the selector to call -forwardInvocation:.
@@ -282,11 +277,6 @@ static void RACCheckTypeEncoding(const char *typeEncoding) {
282
277
}
283
278
}
284
279
285
- static SEL RACAliasOfOriginalForSelector (SEL originalSelector) {
286
- NSString *selectorName = NSStringFromSelector (originalSelector);
287
- return NSSelectorFromString ([RACSignalForSelectorAliasOfOriginalPrefix stringByAppendingString: selectorName]);
288
- }
289
-
290
280
static SEL RACAliasForSelector (SEL originalSelector) {
291
281
NSString *selectorName = NSStringFromSelector (originalSelector);
292
282
return NSSelectorFromString ([RACSignalForSelectorAliasPrefix stringByAppendingString: selectorName]);
@@ -304,6 +294,20 @@ static SEL RACAliasForSelector(SEL originalSelector) {
304
294
return signature.UTF8String ;
305
295
}
306
296
297
+ static NSDictionary * RACSetupInteropImplDictionary (Class baseClass) {
298
+ NSDictionary * interopImpls;
299
+
300
+ @synchronized (baseClass) {
301
+ interopImpls = objc_getAssociatedObject (baseClass, RACInteropImplsKey);
302
+ if (interopImpls == nil ) {
303
+ interopImpls = [[NSMutableDictionary alloc ] init ];
304
+ objc_setAssociatedObject (baseClass, RACInteropImplsKey, interopImpls, OBJC_ASSOCIATION_RETAIN_NONATOMIC );
305
+ }
306
+ }
307
+
308
+ return interopImpls;
309
+ }
310
+
307
311
static Class RACSwizzleClass (NSObject *self) {
308
312
Class statedClass = self.class ;
309
313
Class baseClass = object_getClass (self);
@@ -330,7 +334,7 @@ static Class RACSwizzleClass(NSObject *self) {
330
334
// implementation may be ignorant of methods added to this class.
331
335
@synchronized (swizzledClasses ()) {
332
336
if (![swizzledClasses () containsObject: className]) {
333
- RACSwizzleForwardInvocation (baseClass);
337
+ RACSwizzleForwardInvocation (baseClass, RACSetupInteropImplDictionary (baseClass) );
334
338
RACSwizzleRespondsToSelector (baseClass);
335
339
RACSwizzleGetClass (baseClass, statedClass);
336
340
RACSwizzleGetClass (object_getClass (baseClass), statedClass);
@@ -349,7 +353,7 @@ static Class RACSwizzleClass(NSObject *self) {
349
353
subclass = objc_allocateClassPair (baseClass, subclassName, 0 );
350
354
if (subclass == nil ) return nil ;
351
355
352
- RACSwizzleForwardInvocation (subclass);
356
+ RACSwizzleForwardInvocation (subclass, RACSetupInteropImplDictionary (subclass) );
353
357
RACSwizzleRespondsToSelector (subclass);
354
358
355
359
RACSwizzleGetClass (subclass, statedClass);
0 commit comments