Я выяснил, что Xcode 7 (версия 7.0 (7A220)) изменил порядок, в котором методы +load
для классов и категорий вызываются во время модульных тестов.
Если категория, принадлежащая тестовой цели, реализует метод +load
, она теперь вызывается в конце, когда экземпляры класса могут быть уже созданы и использованы.
У меня есть AppDelegate
, который реализует метод +load
. Файл AppDelegate.m
также содержит категорию AppDelegate (MainModule)
. Кроме того, существует unit test файл LoadMethodTestTests.m
, который содержит другую категорию - AppDelegate (UnitTest)
.
Обе категории также реализуют метод +load
. Первая категория относится к основной цели, вторая - к тестовой цели.
Код
Я сделал небольшой тестовый проект, чтобы продемонстрировать эту проблему. Это пустой проект по умолчанию для 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 test (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
Тестирование
Я выполнил Unit Testing этого проекта (код и ссылка github ниже) на Xcode 6/7 и получил следующий порядок вызовов +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
Вопрос
Xcode 7 запускает метод целевой категории +load
(Unit Test +load
) в конце, после того, как AppDelegate
уже создан.
Правильно ли это, или это ошибка, которую следует отправить Apple?
Может быть, он не указан, поэтому компилятор/среда выполнения могут свободно переупорядочивать звонки?
Я рассмотрел этот вопрос SO, а также описание + load в документации NSObject, но я не совсем понял, как должен работать метод +load
, когда категория принадлежит другой цели.
Или может быть AppDelegate
по какой-то причине является своего рода частным случаем?
Почему я спрашиваю об этом
- Образовательные цели.
- Я использовал метод swizzling в категории внутри цели unit test. Теперь, когда порядок вызова изменился,
applicationDidFinishLaunchingWithOptions
выполняется до того, как происходит swizzling. По-моему, есть другие способы сделать это, но для Xcode 7. мне кажется, что он интуитивно понятен, так как он работает в Xcode 7. Я думал, что когда класс загружается в память,+load
этого класса и+load
методы всех его категорий должны быть вызваны, прежде чем мы сможем что-то с этим классом (например, создать экземпляр и вызватьdidFinishLaunching...
).