XCode 7 xtest(新西兰)和#x2B;加载两次调用的类别方法

XCode 7 xtest(新西兰)和#x2B;加载两次调用的类别方法,xcode,Xcode,在我们的测试框架中(基于Kiwi,而Kiwi又基于XCTest),我们使用NSHipster上描述的“加载时swizzling”技术来切换一些加载时带有模拟的东西。在我们升级到XCode 7之前,它一直工作得很好,现在不知何故,+load方法被调用了两次。据我所知,无论如何都不应该发生这种事 下面是第一个+加载调用的堆栈跟踪(发生在main之前): 这就是第二个调用堆栈的样子: FooTests`+[FooManager(self=FooManager, _cmd="load") load] +

在我们的测试框架中(基于Kiwi,而Kiwi又基于XCTest),我们使用NSHipster上描述的“加载时swizzling”技术来切换一些加载时带有模拟的东西。在我们升级到XCode 7之前,它一直工作得很好,现在不知何故,
+load
方法被调用了两次。据我所知,无论如何都不应该发生这种事

下面是第一个+加载调用的堆栈跟踪(发生在
main
之前):

这就是第二个调用堆栈的样子:

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, delegateClassName=@"AppDelegate") + 227 at ApplicationHooks.m:56
Foo`main(argc=5, argv=0xbfff7778) + 146 at main.mm:15
libdyld.dylib`start + 1
FooTests`+[FooManager(self=FooManager,_cmd=“load”)load]+149在footextensions.mm:152
ibobjc.A.dylib`调用加载方法+292
ibobjc.A.dylib`load_images+129
libdyld.dylib`dlopen+70
CoreFoundation`\u CFBundleDlfcnLoadBundle+185
CoreFoundation`\u CbundleLoadExecutableAndReturnError+336
基金会“-[NSBundle LOADRETURNERROR:+641
XCTest`\u XCTestMain+542
IDEBundleInjection`\uuuuuuuuuxBundleInjection\u块\u调用\u2+20
CoreFoundation`\uuu CFRUNLOOP\u正在调用\u OUT\u到\u块\uuu+16
CoreFoundation`\uu CFRunLoopDoBlocks+195
CoreFoundation`\uuu CFRunLoopRun+1016
CoreFoundation`CFRunLoopRunSpecific+470
CoreFoundation`CFRunLoopRunInMode+123
GraphicsServices`GSEventRunModal+192
GraphicsServices`GSEventRun+104
UIKit`UIApplicationMain+160
Foo`UIApplicationMain(argc=,argv=,principalClassName=0x00000000,delegateClassName=@“AppDelegate”)+227位于ApplicationHooks。m:56
Foo`main(argc=5,argv=0xbff7778)+146位于main.mm:15
libdyld.dylib`start+1
看起来动态加载程序最初会像预期的那样调用
+load
方法,但是XCTest运行时会再次调用它们

问题是,即使是
dispatch\u once
也不能工作,因为静态变量似乎没有正确启动,所以
dispatch\u once\u t
标记在
+load
调用之间会发生变化!唯一的工作是创建一个C++类,并授权<代码> DeXCHYON/<代码>调用它(使用一个适当的C++静态变量,用于<代码> DeXCHCHONCECET)。 编辑-我很确定这是相关的,但我不认为更改顺序会导致它运行两次

EDIT2-这种行为似乎并不新鲜。从a中的评论:

如果您正在运行注入应用程序的测试包,并且 应用程序和测试包都链接到同一个.a文件,任何加载 一个文件将被触发两次


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

原因可能有很多:

  • .mm文件包含在两个目标中
  • 由于测试过程的变化,Xcode加载测试二进制文件两次
  • 测试目标和应用程序链接到同一个。一个包含
    +load
    方法的文件

非常确定您的捆绑包在要加载的调用之间被卸载。尝试创建一个
\uuuuuuu属性((析构函数))
函数,看看是否正确。如果我的理论是正确的,您可以通过让您的测试目标不链接到具有该类别的框架,或者通过使用
dlopen
和friends进行运行时链接来解决它。感谢您的快速响应!我实现了这里描述的构造函数和析构函数:并在其中的每一个中激发了一个NSLog。有趣的是,包从未卸载,但它确实加载了两次。有鉴于此,我还应该试试你的变通方法吗?如果是这样,我的测试目标需要具有该类别的库(就像我说的,我们使用它在
+load
上用mock swizzle一些东西),所以我想我应该走
dlopen
路线?顺便问一句,包被加载两次是否一定是件坏事?否则,也许我可以在应用程序委托中进行swizzling(
didFinishLaunchingWithOptions
)?我认为原因甚至比这更简单-我确实在链接捆绑包,包括应用程序和测试目标中的类扩展(参见我的上一次编辑)。
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, delegateClassName=@"AppDelegate") + 227 at ApplicationHooks.m:56
Foo`main(argc=5, argv=0xbfff7778) + 146 at main.mm:15
libdyld.dylib`start + 1