In OCMockito, test doubles are implemented with NSProxy. A double standing in for an instance implements -respondsToSelector:
as follows:
- (BOOL)respondsToSelector:(SEL)aSelector {
return [_mockedClass instancesRespondToSelector:aSelector];
}
But a double standing in for a class implements -respondsToSelector:
like this:
- (BOOL)respondsToSelector:(SEL)aSelector {
return [_mockedClass respondsToSelector:aSelector];
}
This all works in the 32-bit runtime. For example, if _mockedClass
is [NSString class]
, the proxy correctly answers that it responds to the selector +pathWithComponents:
But in the 64-bit runtime, it crashes:
Crashed Thread: 0 Dispatch queue: com.apple.main-thread
Exception Type: EXC_BAD_ACCESS (SIGSEGV)
Exception Codes: EXC_I386_GPFLT
Application Specific Information:
objc[1868]: GC: forcing GC OFF because OBJC_DISABLE_GC is set
Thread 0 Crashed:: Dispatch queue: com.apple.main-thread
0 libobjc.A.dylib 0x00007fff95cbffc6 cache_getImp + 6
1 libobjc.A.dylib 0x00007fff95ccd1dc lookUpImpOrForward + 50
2 libobjc.A.dylib 0x00007fff95ccd198 lookUpImpOrNil + 20
3 libobjc.A.dylib 0x00007fff95cc218a class_respondsToSelector + 37
4 com.apple.CoreFoundation 0x00007fff91c131ad ___forwarding___ + 429
5 com.apple.CoreFoundation 0x00007fff91c12f78 _CF_forwarding_prep_0 + 120
6 org.mockito.OCMockitoTests 0x000000010451a55b -[StubClassTest testStubbedMethod_ShouldReturnGivenObject] + 107 (StubClassTest.m:48)
Note that it's calling class_respondsToSelector(…)
. I suspect that I'm being bitten by an optimization made to the runtime. What can I do to fix this?
it's a bit long answer, so bear with me. I ran a simple code just to verify the behavior:
Class mock = mockClass([NSProcessInfo class]);
[mock processInfo];
[verify(mock) processInfo];
Indeed It does crash with bad pointer exception. Replacing first line with
id mock = mockClass([NSProcessInfo class]);
works as expected. I figured that it might be worth to look at the code after ARC. Those snippets are a bit to long, so here are the gists: Class
-based test, id
-based test
As you can see, when you declare variable of type Class
there is an extra release
. My guess is that since classes are registered for the entire runtime duration (unless removed using runtime api) it's ok to have Class
variable as __unsafe_unretained
.
To summarize, you have two possible solutions:
@implementation StubClassTest
{
__strong Class mockClass;
}
or
@implementation StubClassTest
{
id mockClass;
}
seem to fix the issue for me.
Update
As a special case, if the object’s base type is Class (possibly protocol-qualified), the type is adjusted to have __unsafe_unretained qualification instead.
From http://clang.llvm.org/docs/AutomaticReferenceCounting.html#objects
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With