Ho scoperto che Xcode 7 (Versione 7.0 (7A220)) ha cambiato l'ordine in cui i metodi +load
per le classi e le categorie vengono chiamati durante i test unitari.Ordine di metodo + carico modificato in Xcode 7
Se una categoria appartenente al target di test implementa un metodo +load
, viene ora chiamato alla fine, quando potrebbero essere già state create e utilizzate istanze della classe.
Ho un AppDelegate
, che implementa il metodo +load
. Il file AppDelegate.m
contiene anche la categoria AppDelegate (MainModule)
. Inoltre, esiste un file di test unitario LoadMethodTestTests.m
, che contiene un'altra categoria - AppDelegate (UnitTest)
.
Entrambe le categorie implementano anche il metodo +load
. La prima categoria appartiene all'obiettivo principale, il secondo - all'obiettivo del test.
Codice
ho fatto un piccolo test project per dimostrare il problema. Si tratta di un progetto di visualizzazione Xcode predefinito vuoto con solo due file modificati.
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
E un file di test di unità (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
ho eseguito Unit Testing di questo progetto (il codice e la github link is below) su Xcode 6/7 e ha ottenuto il seguente ordine +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
Domanda
Xcode 7 corre il metodo categoria +load
bersaglio di prova (Unit Test +load
), alla fine, dopo che la AppDelegate
è già stato creato. È un comportamento corretto o è un bug che dovrebbe essere inviato ad Apple?
Può essere che non sia specificato, quindi il compilatore/runtime è libero di riorganizzare le chiamate? Ho dato un'occhiata allo this SO question e allo +load description in the NSObject documentation ma non ho capito bene come si suppone che il metodo +load
funzioni quando la categoria appartiene a un altro target.
O può essere AppDelegate
è una sorta di caso speciale per qualche motivo?
questo che sto chiedendo questo
- scopi didattici.
- Ho usato per eseguire il metodo di swizzling in una categoria all'interno del target di test unitario. Ora, quando l'ordine di chiamata è cambiato,
applicationDidFinishLaunchingWithOptions
viene eseguito prima che abbia luogo lo swizzling. Ci sono altri modi per farlo, credo, ma mi sembra semplicemente controintuitivo il modo in cui funziona in Xcode 7.Ho pensato che quando una classe viene caricata in memoria, è necessario chiamare+load
di questa classe e i metodi+load
di tutte le sue categorie prima di poter qualcosa con questa classe (come creare un'istanza e chiamaredidFinishLaunching...
).
Grazie mille! Per essere onesti, non mi aspettavo di ottenere una risposta così rapida e accurata per questa domanda. Sono fortunato :) Inoltre, grazie per i suggerimenti proposti. – FreeNickname
Interessante. Questo spiegherebbe che i metodi '+ load' vengano chiamati due volte come sto vedendo qui? http://stackoverflow.com/questions/33154729/xcode-7-xctestkiwi-load-category-methods-called-twice –
Ho un'altra domanda per te se non ti dispiace. Hai detto che è una buona pratica usare un host separato per i test unitari. Ho provato a utilizzare materiali Google per le best practice sui test dell'unità iOS, ma sfortunatamente non ho trovato nulla sull'argomento. Avete collegamenti a qualsiasi materiale in cui potrei leggere i modi migliori per implementarlo? Ho provato a duplicare semplicemente il target principale, ma sembra che in questo modo dovrò mantenere manualmente i target sincronizzati. Pensavo che dovesse esserci un modo migliore. – FreeNickname