2015-10-15 62 views
1

在我们的测试框架中(基于Kiwi,而Kiwi又基于XCTest),我们使用NSHipster here上描述的“swizzling on load”技术来切换一些东西与嘲笑负载。直到我们升级到XCode 7之前它一直运行良好,现在不知何故+load方法被调用两次。据我了解,不管发生什么都不应该发生?XCode 7 XCTest(Kiwi)+加载类方法调用两次

这里的第一个+负载调用的堆栈跟踪(发生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 

它看起来像动态loader最初按预期调用+load方法,但是然后XCTest运行时再次调用它们。

事情是,即使dispatch_once不起作用,因为静态变量似乎没有正确启动,因此dispatch_once_t令牌在+load调用之间变化!唯一有效的工作是创建一个C++类并将dispatch_once调用委托给它(对于dispatch_once_t使用适当的C++静态变量)。

编辑 - 我很确定这个+load order change是相关的,但我不知道如何改变顺序会导致它运行两次。

EDIT2 - 看来,这种行为是不是新的。从在related blog post评论:

如果您运行的是注入到一个应用程序的测试包,并 无论是应用和测试包链接到同一个某文件,在任何负载 方法.a文件将被触发两次。

+0

很确定您的包正在被加载的调用之间卸载。尝试创建一个'__attribute __((析构函数))'函数,看看是否如此。如果我的理论是正确的,你可以通过让测试目标不链接到具有类别的框架,或者通过使用'dlopen'和朋友的运行时链接来解决它。 –

+0

感谢您的快速响应!我实现了构造函数和析构函数,如下所述:http://stackoverflow.com/q/2053029/67824并在它们中的每一个中引发了NSLog。有趣的是,捆绑包从未卸载过,但它确实加载了两次。鉴于此,我还应该尝试一下你的解决方法吗?如果是这样,我的测试目标需要具有该类别的库(就像我说过我们使用'+ load'来模拟一些东西),所以我想我应该去'dlopen'路线? –

+0

顺便说一句,这个软件包加载两次肯定是件坏事?否则,也许我可以放弃在应用程序委托('didFinishLaunchingWithOptions')中进行swizzling? –

回答

2

看起来您在主应用程序二进制文件和测试包中都包含相同的类扩展。这也可以解释为什么你在第二次加载时看到静态dispatch_once标记的单独设置。

为什么会出现这种情况可能是许多事情:

  • .mm文件包含在两个目标
  • Xcode中加载测试二进制两次因测试程序的变化
  • 测试目标和应用程序都链接到包含+load方法的相同.a文件
+0

我认为原因比这更简单 - 我真的在包括应用程序和测试目标中的类扩展(请参阅我的上次编辑)中捆绑链接。 –