7

在 OCMockito 中,测试替身是使用 NSProxy 实现的。双重替补实例的实现-respondsToSelector:方式如下:

- (BOOL)respondsToSelector:(SEL)aSelector {
    return [_mockedClass instancesRespondToSelector:aSelector];
}

但是一个的双重代表-respondsToSelector:是这样的:

- (BOOL)respondsToSelector:(SEL)aSelector {
    return [_mockedClass respondsToSelector:aSelector];
}

这一切都适用于 32 位运行时。例如,如果_mockedClass[NSString class],代理正确回答它响应选择器+pathWithComponents:

但在 64 位运行时,它会崩溃:

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)

请注意,它正在调用class_respondsToSelector(…). 我怀疑我被运行时的优化所困扰。我能做些什么来解决这个问题?

4

1 回答 1

7

这是一个有点长的答案,所以请耐心等待。我运行了一个简单的代码来验证行为:

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

于 2014-05-28T13:23:40.847 回答