В нашей тестовой среде (на основе Kiwi, которая, в свою очередь, основана на XCTest), мы используем технику "swizzling on load", описанную в NSHipster здесь, чтобы переключить некоторые вещи с помощью mocks при загрузке. Он работает достаточно хорошо, пока мы не перешли на XCode 7, и теперь методы +load
вызываются дважды. Насколько я понимаю, это никогда не должно произойти ни на что?
Здесь трассировка стека первого вызова + нагрузки (выполняется до main
):
Foo`+[FooManager(self=FooManager, _cmd="load") load] + 149 at FooExtensions.mm:152
libobjc.A.dylib`call_load_methods + 292
libobjc.A.dylib`load_images + 129
...
dyld`dyld::useSimulatorDyld(int, macho_header const*, char const*, int, char const**, char const**, char const**, unsigned long*) + 1053
dyld`dyld::_main(macho_header const*, unsigned long, int, char const**, char const**, char const**, unsigned long*) + 202
dyld`dyldbootstrap::start(macho_header const*, int, char const**, long, macho_header const*, unsigned long*) + 428
dyld`_dyld_start + 71
И вот как выглядит второй столбец:
FooTests`+[FooManager(self=FooManager, _cmd="load") load] + 149 at FooExtensions.mm:152
ibobjc.A.dylib`call_load_methods + 292
ibobjc.A.dylib`load_images + 129
libdyld.dylib`dlopen + 70
CoreFoundation`_CFBundleDlfcnLoadBundle + 185
CoreFoundation`_CFBundleLoadExecutableAndReturnError + 336
Foundation`-[NSBundle loadAndReturnError:] + 641
XCTest`_XCTestMain + 542
IDEBundleInjection`____XCBundleInjection_block_invoke_2 + 20
CoreFoundation`__CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__ + 16
CoreFoundation`__CFRunLoopDoBlocks + 195
CoreFoundation`__CFRunLoopRun + 1016
CoreFoundation`CFRunLoopRunSpecific + 470
CoreFoundation`CFRunLoopRunInMode + 123
GraphicsServices`GSEventRunModal + 192
GraphicsServices`GSEventRun + 104
UIKit`UIApplicationMain + 160
Foo`UIApplicationMain(argc=<unavailable>, argv=<unavailable>, principalClassName=0x00000000, [email protected]"AppDelegate") + 227 at ApplicationHooks.m:56
Foo`main(argc=5, argv=0xbfff7778) + 146 at main.mm:15
libdyld.dylib`start + 1
Похоже, что динамический загрузчик изначально вызывает методы +load
, как ожидалось, но затем время выполнения XCTest вызывает их снова.
Дело в том, что даже dispatch_once
не работает, поскольку статические переменные, похоже, не инициируются должным образом, поэтому токены dispatch_once_t
изменяются между +load
invocations! Единственное, что сработало, это создать класс С++ и делегировать ему вызов dispatch_once
(используя старую переменную С++ для dispatch_once_t
).
EDIT - я уверен, что это +load
изменение заказа связано, но я не вижу, как изменение порядка привело бы к его запуску в два раза.
EDIT2 - Кажется, что это поведение не нова. Из комментария в связанном сообщении в блоге:
Если вы используете тестовый пакет, который вводится в приложение, и как приложение, так и ссылка тестового пакета в тот же файл .a, любая загрузка метод в том, что файл .a будет запущен дважды.