XCode 7 XCTest(Kiwi) +加载类别方法调用两次 [英] XCode 7 XCTest(Kiwi) +load category methods called twice

查看:22
本文介绍了XCode 7 XCTest(Kiwi) +加载类别方法调用两次的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在我们的测试框架(基于 Kiwi,而后者又基于 XCTest)中,我们使用了 NSHipster 上描述的swizzling on load"技术here 在加载时切换一些带有模拟的东西.它一直运行良好,直到我们升级到 XCode 7,现在不知何故 +load 方法被调用了两次.据我了解,无论如何这都不应该发生?

In our testing framework (based on Kiwi, which in turn is based on XCTest), we're using the "swizzling on load" technique described on NSHipster here to switch some stuff with mocks on load. It has been working well enough until we upgraded to XCode 7, and now somehow +load methods are called twice. As far as I understand this should never happen no matter what?

这是第一个 +load 调用的堆栈跟踪(发生在 main 之前):

Here's the stack trace of the first +load call (takes place before 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

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

And this is how the second callstack looks like:

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

看起来动态加载器最初按预期调用了 +load 方法,但随后 XCTest 运行时再次调用它们.

It looks like the dynamic loader initially calls the +load methods as expected, but then the XCTest runtime calls them again.

问题是,即使 dispatch_once 也不起作用,因为静态变量似乎没有正确启动,所以 dispatch_once_t 令牌在 +load<之间变化/code> 调用!唯一有效的是创建一个 C++ 类并将 dispatch_once 调用委托给它(对 dispatch_once_t 使用适当的 C++ 静态变量).

The thing is, even dispatch_once doesn't work as static variables don't seem to be initiated properly so the dispatch_once_t tokens change between +load invocations! The only thing that worked was creating a C++ class and delegating the dispatch_once call to it (using a proper c++ static variable for the dispatch_once_t).

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

EDIT - I'm pretty sure this +load order change is related, but I don't see how changing the order would have caused it to be run twice.

EDIT2 - 这种行为似乎并不新鲜.来自 相关博文:

EDIT2 - It seems that this behavior is not new. From a comment in a related blog post:

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

If you are running a test bundle that is injected into an app, and both the app and the test bundle link to the same .a file, any load method in that .a file will be triggered twice.

推荐答案

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

It appears that you have included the same class extension in both the main application binary and in your test bundle. This would also explain why you see a separate setup of static dispatch_once tokens on the second load.

出现这种情况的原因可能有很多:

Why this is the case may be a number of things:

  • .mm-file 包含在两个目标中
  • 由于测试过程的变化,Xcode 两次加载测试二进制文件
  • 测试目标和应用程序都链接到包含 +load 方法的同一个 .a 文件
  • .mm-file is included in both targets
  • Xcode loads the test binary twice due to changes in test-procedure
  • Both test target and application link to the same .a file containing the +load method

这篇关于XCode 7 XCTest(Kiwi) +加载类别方法调用两次的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆