在Xcode 7中更改+加载方法的顺序
我发现Xcode 7(版本7.0(7A220))在unit testing中改变了调用类和类的+load
方法的顺序。
如果属于testing目标的类别实现了一个+load
方法,那么现在在最后调用该类时,该类的实例可能已经被创build和使用。
我有一个AppDelegate
,它实现+load
方法。 AppDelegate.m
文件还包含AppDelegate (MainModule)
类别。 此外,还有一个unit testing文件LoadMethodTestTests.m
,它包含另一个类别 – AppDelegate (UnitTest)
。
这两个类别也实现+load
方法。 第一类属于主要目标,第二类属于testing目标。
码
我做了一个小testing项目来演示这个问题。 这是一个空的默认Xcode一个视图项目,只有两个文件被更改。
AppDelegate.m:
#import "AppDelegate.h" @implementation AppDelegate +(void)load { NSLog(@"Class load"); } - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { NSLog(@"didFinishLaunchingWithOptions"); return YES; } @end @interface AppDelegate (MainModule) @end @implementation AppDelegate (MainModule) +(void)load { NSLog(@"Main Module +load"); } @end
和一个unit testing文件(LoadMethodTestTests.m):
#import <UIKit/UIKit.h> #import <XCTest/XCTest.h> #import "AppDelegate.h" @interface LoadMethodTestTests : XCTestCase @end @interface AppDelegate (UnitTest) @end @implementation AppDelegate (UnitTest) +(void)load { NSLog(@"Unit Test +load"); } @end @implementation LoadMethodTestTests -(void)testEmptyTest { XCTAssert(YES); } @end
testing
我在Xcode 6/7上执行了这个项目的unit testing(代码和github链接如下),并获得了以下+load
调用顺序:
Xcode 6 (iOS 8.4 simulator): Unit Test +load Class load Main Module +load didFinishLaunchingWithOptions Xcode 7 (iOS 9 simulator): Class load Main Module +load didFinishLaunchingWithOptions Unit Test +load Xcode 7 (iOS 8.4 simulator): Class load Main Module +load didFinishLaunchingWithOptions Unit Test +load
题
在AppDelegate
已经被创build之后,Xcode 7最后运行testing目标类别+load
方法( Unit Test +load
)。 这是一个正确的行为还是应该发送给苹果的错误?
可能是没有指定,所以编译器/运行时是自由重新安排调用? 我看了这个问题以及NSObject文档中的+ load描述,但是我不太明白当类别属于另一个目标时+load
方法应该如何工作。
或者可能由于某种原因AppDelegate
是某种特殊情况?
我为什么这么问
- 教育目的。
- 我曾经在unit testing目标内的一个类别中执行方法debugging。 现在,当调用顺序发生变化时,在调整之前执行
applicationDidFinishLaunchingWithOptions
。 我相信还有其他方法可以做到这一点,但对Xcode 7来说,这似乎是违反直觉的。我认为,当一个类被加载到内存中时,+load
这个类并+load
所有的方法它的类别应该被调用之前,我们可以与这个类的东西(如创build一个实例,并调用didFinishLaunching...
)。
TL,DR:这是xctest的错,不是objc的。
这是因为xctest
可执行文件(实际运行unit testing的可执行文件,位于$XCODE_DIR/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/Library/Xcode/Agents/xctest
加载了它的包。
Pre-Xcode 7在运行任何testing之前加载了所有引用的testing包。 这可以看出(对于那些关心),通过反汇编Xcode 6.4的二进制文件,可以看到符号-[XCTestTool runTestFromBundle:]
的相关部分。
在Xcode 7版本的xctest
,你可以看到它延迟了testing包的加载,直到实际的testing由XCTestSuite
运行,在实际的XCTest
框架中,可以在符号__XCTestMain
看到,该符号仅在testing主机应用程序正在build立。
因为这些被调用的内部顺序发生了变化,所以调用你的testing的+load
方法的方式是不同的。 没有对Objective-c-runtime的内部进行修改。
如果你想在你的应用程序中解决这个问题,你可以做一些事情。 首先,你可以使用+[NSBundle bundleWithPath:]
手动加载你的bundle,然后调用-load
。
你也可以将你的testing目标链接回你的testing主机应用程序(我希望你正在使用一个独立的testing主机,而不是你的主应用程序!),这会在xctest加载主机应用程序时自动加载。
我不认为它是一个错误,它只是XCTest的一个实现细节。
来源:只需花费最近3天拆解xctest
一个完全不相关的理由。
Xcode 7在iOS模板项目中有两个不同的加载顺序。
unit testing用例。 对于unit testing,在应用程序启动到主屏幕之后,将testing包注入到正在运行的模拟中。 默认的unit testing执行顺序如下所示:
Application: AppDelegate initialize() Application: AppDelegate init() Application: AppDelegate application(…didFinishLaunchingWithOptions…) Application: ViewController viewDidLoad() Application: ViewController viewWillAppear() Application: AppDelegate applicationDidBecomeActive(…) Application: ViewController viewDidAppear() Unit Test: setup() Unit Test: testExample()
UItesting用例。 对于UItesting, 单独的第二个进程 XCTRunner
被设置,它执行被testing的应用程序。 一个参数可以从testingsetUp()
传递…
class Launch_UITests: XCTestCase { override func setUp() { // … other code … let app = XCUIApplication() app.launchArguments = ["UI_TESTING_MODE"] app.launch() // … other code … }
被收到的AppDelegate
…
class AppDelegate: UIResponder, UIApplicationDelegate { func application(… didFinishLaunchingWithOptions… ) -> Bool { // … other code … let args = NSProcessInfo.processInfo().arguments if args.contains("UI_TESTING_MODE") { print("FOUND: UI_TESTING_MODE") } // … other code …
可以通过从testing代码和应用程序代码中打印NSProcessInfo.processInfo().processIdentifier
和NSProcessInfo.processInfo().processName
in来观察对单独进程的注入 。
- 在Swift中,我怎么能声明一个符合一个或多个协议的特定types的variables?
- 在UITableViewCell中自定义VoiceOver操作
- 从子类访问Root类中的variables的值
- Pushwoosh phonegap插件,检索设备ID
- 在Parse中保存多个对象需要多个API请求?
- 在iOS上,有没有办法只search某个标签的子视图?
- Phonegap 3.4 FileTransfer错误(iOS)
- UItesting失败 – 无论是元素还是任何后代都没有关注secureTextField的键盘
- (viewConroller.view removeFromSuperview)线程:1 EXC_BAD_ACCESS(代码= 1,地址= 0x6000000008)