NSProxy, претендующий на класс, не обрабатывает отклики в 64-битной среде
В 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(…)
, Я подозреваю, что меня укусила оптимизация, сделанная во время выполнения. Что я могу сделать, чтобы это исправить?
1 ответ
Это немного длинный ответ, так что терпите меня. Я запустил простой код, чтобы проверить поведение:
Class mock = mockClass([NSProcessInfo class]);
[mock processInfo];
[verify(mock) processInfo];
Действительно, происходит сбой с неправильным указателем исключения. Замена первой строки
id mock = mockClass([NSProcessInfo class]);
работает как положено. Я подумал, что, возможно, стоит взглянуть на код после ARC. Эти фрагменты немного длинны, поэтому вот суть: Class
тест, id
тест
Как вы можете видеть, когда вы объявляете переменную типа Class
есть дополнительный release
, Я предполагаю, что, так как классы зарегистрированы в течение всего времени выполнения (если они не были удалены с помощью API времени выполнения), все в порядке Class
переменная как __unsafe_unretained
,
Подводя итог, у вас есть два возможных решения:
@implementation StubClassTest
{
__strong Class mockClass;
}
или же
@implementation StubClassTest
{
id mockClass;
}
кажется, решить проблему для меня.
Обновить
В особом случае, если базовым типом объекта является класс (возможно, квалифицированный по протоколу), тип настраивается таким образом, чтобы вместо него иметь квалификацию __unsafe_unretained.
От http://clang.llvm.org/docs/AutomaticReferenceCounting.html