@@ -87,8 +87,11 @@ - (void)stopMocking
8787
8888- (void )stopMockingClassMethods
8989{
90- OCMSetAssociatedMockForClass (nil , mockedClass);
91- object_setClass (mockedClass, originalMetaClass);
90+ @synchronized (mockedClass)
91+ {
92+ OCMSetAssociatedMockForClass (nil , mockedClass);
93+ object_setClass (mockedClass, originalMetaClass);
94+ }
9295 originalMetaClass = nil ;
9396 /* created meta class will be disposed later because partial mocks create another subclass depending on it */
9497}
@@ -119,48 +122,49 @@ - (void)prepareClassForClassMethodMocking
119122 if (otherMock != nil )
120123 [otherMock stopMockingClassMethods ];
121124
122- OCMSetAssociatedMockForClass (self, mockedClass);
123125
124126 /* dynamically create a subclass and use its meta class as the meta class for the mocked class */
125127 classCreatedForNewMetaClass = OCMCreateSubclass (mockedClass, mockedClass);
126128 originalMetaClass = object_getClass (mockedClass);
127129 id newMetaClass = object_getClass (classCreatedForNewMetaClass);
128-
129130 /* create a dummy initialize method */
130131 Method myDummyInitializeMethod = class_getInstanceMethod ([self mockObjectClass ], @selector (initializeForClassObject ));
131132 const char *initializeTypes = method_getTypeEncoding (myDummyInitializeMethod);
132133 IMP myDummyInitializeIMP = method_getImplementation (myDummyInitializeMethod);
133134 class_addMethod (newMetaClass, @selector (initialize ), myDummyInitializeIMP, initializeTypes);
134-
135- object_setClass (mockedClass, newMetaClass); // only after dummy initialize is installed (iOS9)
136-
137- /* point forwardInvocation: of the object to the implementation in the mock */
138- Method myForwardMethod = class_getInstanceMethod ([self mockObjectClass ], @selector (forwardInvocationForClassObject: ));
139- IMP myForwardIMP = method_getImplementation (myForwardMethod);
140- class_addMethod (newMetaClass, @selector (forwardInvocation: ), myForwardIMP, method_getTypeEncoding (myForwardMethod));
141-
142- /* adding forwarder for most class methods (instance methods on meta class) to allow for verify after run */
143- NSArray *methodBlackList = @[
144- @" class" , @" forwardingTargetForSelector:" , @" methodSignatureForSelector:" , @" forwardInvocation:" , @" isBlock" ,
145- @" instanceMethodForwarderForSelector:" , @" instanceMethodSignatureForSelector:" , @" resolveClassMethod:"
146- ];
147- void (^setupForwarderFiltered)(Class , SEL ) = ^(Class cls, SEL sel) {
148- if ((cls == object_getClass ([NSObject class ])) || (cls == [NSObject class ]) || (cls == object_getClass (cls)))
149- return ;
150- if (OCMIsApplePrivateMethod (cls, sel))
151- return ;
152- if ([methodBlackList containsObject: NSStringFromSelector (sel)])
153- return ;
154- @try
155- {
156- [self setupForwarderForClassMethodSelector: sel];
157- }
158- @catch (NSException *e)
159- {
160- // ignore for now
161- }
162- };
163- [NSObject enumerateMethodsInClass: originalMetaClass usingBlock: setupForwarderFiltered];
135+ @synchronized (mockedClass)
136+ {
137+ object_setClass (mockedClass, newMetaClass); // only after dummy initialize is installed (iOS9)
138+ OCMSetAssociatedMockForClass (self, mockedClass);
139+
140+ /* point forwardInvocation: of the object to the implementation in the mock */
141+ Method myForwardMethod = class_getInstanceMethod ([self mockObjectClass ], @selector (forwardInvocationForClassObject: ));
142+ IMP myForwardIMP = method_getImplementation (myForwardMethod);
143+ class_addMethod (newMetaClass, @selector (forwardInvocation: ), myForwardIMP, method_getTypeEncoding (myForwardMethod));
144+
145+ /* adding forwarder for most class methods (instance methods on meta class) to allow for verify after run */
146+ NSArray *methodBlackList = @[
147+ @" class" , @" forwardingTargetForSelector:" , @" methodSignatureForSelector:" , @" forwardInvocation:" , @" isBlock" ,
148+ @" instanceMethodForwarderForSelector:" , @" instanceMethodSignatureForSelector:" , @" resolveClassMethod:"
149+ ];
150+ void (^setupForwarderFiltered)(Class , SEL ) = ^(Class cls, SEL sel) {
151+ if ((cls == object_getClass ([NSObject class ])) || (cls == [NSObject class ]) || (cls == object_getClass (cls)))
152+ return ;
153+ if (OCMIsApplePrivateMethod (cls, sel))
154+ return ;
155+ if ([methodBlackList containsObject: NSStringFromSelector (sel)])
156+ return ;
157+ @try
158+ {
159+ [self setupForwarderForClassMethodSelector: sel];
160+ }
161+ @catch (NSException *e)
162+ {
163+ // ignore for now
164+ }
165+ };
166+ [NSObject enumerateMethodsInClass: originalMetaClass usingBlock: setupForwarderFiltered];
167+ }
164168}
165169
166170
@@ -184,15 +188,18 @@ - (void)setupForwarderForClassMethodSelector:(SEL)selector
184188- (void )forwardInvocationForClassObject : (NSInvocation *)anInvocation
185189{
186190 // in here "self" is a reference to the real class, not the mock
187- OCClassMockObject *mock = OCMGetAssociatedMockForClass ((Class )self, YES );
188- if (mock == nil )
189- {
190- [NSException raise: NSInternalInconsistencyException format: @" No mock for class %@ " , NSStringFromClass ((Class )self )];
191- }
192- if ([mock handleInvocation: anInvocation] == NO )
191+ @synchronized (self)
193192 {
194- [anInvocation setSelector: OCMAliasForOriginalSelector ([anInvocation selector ])];
195- [anInvocation invoke ];
193+ OCClassMockObject *mock = OCMGetAssociatedMockForClass ((Class )self, YES );
194+ if (mock == nil )
195+ {
196+ [anInvocation invoke ];
197+ }
198+ else if ([mock handleInvocation: anInvocation] == NO )
199+ {
200+ [anInvocation setSelector: OCMAliasForOriginalSelector ([anInvocation selector ])];
201+ [anInvocation invoke ];
202+ }
196203 }
197204}
198205
0 commit comments