2017-10-20 1 views
2

Unsere App erfordert eine leichtgewichtige Kerndaten-Migration, da wir unseren Entitäten einige Attribute hinzugefügt haben.Core Data Light Weight Migration ohne beim Start getötet zu werden

Nach der Freigabe des Updates zu unseren Beta-Testern auf TestFlight hatten wir Berichte von einigen von ihnen, dass die App beim Start abstürzte. Nachdem wir die Absturzprotokolle bekommen hatten, stellten wir fest, dass der Sprungbrett-Wachhund die App kaputt machte, weil die Migration zu lange dauerte.

Nach der Online-Suche nach Ressourcen scheint es möglich zu sein, die Migration außerhalb von application:didFinishLaunchingWithOptions: zu verschieben, indem zunächst überprüft wird, ob eine Migration erforderlich ist, der Core Data-Stack nicht berührt wird und die Migration in einem anderen View-Controller ausgeführt wird . Hier ist das, was ich zu tun versucht:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 

    if ([[ZSSCoreDataManager sharedService] migrationRequired]) { 

     UpgradeDatabaseViewController *upgrade = [self.window.rootViewController.storyboard instantiateViewControllerWithIdentifier:@"UpgradeDatabaseViewController"]; 
     self.window.rootViewController = upgrade; 

    } 

    return YES; 

} 

Migration Test:

- (BOOL)migrationRequired { 

    NSManagedObjectModel *mom = [NSManagedObjectModel mergedModelFromBundles:nil]; 
    NSPersistentStoreCoordinator *persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:mom]; 

    NSError *error = nil; 
    NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:@"CoreDataMergeTest.sqlite"]; 

    // Determine if a migration is needed 
    NSDictionary *sourceMetadata = [NSPersistentStoreCoordinator metadataForPersistentStoreOfType:NSSQLiteStoreType URL:storeURL options:nil error:&error]; 
    NSManagedObjectModel *destinationModel = [persistentStoreCoordinator managedObjectModel]; 
    BOOL pscCompatibile = [destinationModel isConfiguration:nil compatibleWithStoreMetadata:sourceMetadata]; 

    return !pscCompatibile; 

} 

UpgradeDatabaseViewController.m

- (void)viewDidLoad { 
    [super viewDidLoad]; 

    NSLog(@"Starting migration"); 

    // Start migration by accessing th epersistent container 
    [[ZSSCoreDataManager sharedService] persistentContainer]; 

    NSLog(@"Ended migration"); 

    UIWindow *window = [UIApplication sharedApplication].keyWindow; 
    UINavigationController *nav = [self.storyboard instantiateViewControllerWithIdentifier:@"MainNav"]; 
    window.rootViewController = nav; 

} 

Persistent Container:

- (NSPersistentContainer *)persistentContainer { 

    @synchronized (self) { 

     if (_persistentContainer != nil) { 
      return _persistentContainer; 
     } 

     _persistentContainer = [[NSPersistentContainer alloc] initWithName:@"Model"]; 

     // Store description 
     NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:@"CoreDataMergeTest.sqlite"]; 
     NSPersistentStoreDescription *description = [NSPersistentStoreDescription persistentStoreDescriptionWithURL:storeURL]; 

     description.shouldInferMappingModelAutomatically = YES; 
     description.shouldMigrateStoreAutomatically = YES; 

     description.type = NSSQLiteStoreType; 
     _persistentContainer.persistentStoreDescriptions = @[description]; 

     [_persistentContainer loadPersistentStoresWithCompletionHandler:^(NSPersistentStoreDescription *storeDescription, NSError *error){ 
      if (error != nil) { 
       NSLog(@"Unresolved error %@, %@", error, error.userInfo); 
       //abort(); 
      } 
     }]; 

     _persistentContainer.viewContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy; 
     _persistentContainer.viewContext.undoManager = nil; // We don't need undo so set it to nil. 
     _persistentContainer.viewContext.shouldDeleteInaccessibleFaults = YES; 
     _persistentContainer.viewContext.automaticallyMergesChangesFromParent = YES; 

    } 

    return _persistentContainer; 

} 

Leider noch diese doesn schein die iss zu lösen Da das Sprungbrett die App immer noch auslöst (wenn das Gerät nicht angeschlossen ist und der Debugger läuft).

Gibt es hier etwas, was ich falsch gemacht habe? Erfolgt die Lightweight-Migration in application:didFinishLaunchingWithOptions: unabhängig davon, ob wir unsere Datenbank nicht initiieren?

Ist es sogar möglich, das zu tun, was ich bei der Verwendung der Lightweight-Migration tun möchte?

Antwort

1

Migration tritt auf, wenn Sie den persistenten Speicher laden. Mit NSPersistentContainer passiert es, wenn Sie loadPersistentStores() anrufen. In Ihrem Code sieht es so aus, als wäre es wahrscheinlich in ZSSCoreDataManager 's persistentContainer Methode.

Es geht nicht darum, was Methode Sie diese auf zu tun, ist es eine Frage, was Warteschlange Sie es fordern. Der Watchdog wird Ihre App beenden, wenn Sie die Hauptwarteschlange für eine lange Zeit blockieren. Wenn Sie dies tun, reagiert Ihre App nicht mehr. Watchdog kann nicht sagen, warum das passiert, und die Regel, die es erzwingt, ist, dies als einen verhassten Prozess zu behandeln und ihn zu töten. Dies ist im Allgemeinen eine gute Idee, da es nur passiert, wenn Ihre App lange Zeit dort verweilt und Benutzereingaben ignoriert. Wenn Sie mit der Migration in einer viewDidLoad umgehen, befinden Sie sich in der Hauptwarteschlange, weshalb Ihre App getötet wird.

Ich bin mir nicht sicher, ob dies mit nur NSPersistentContainer behoben werden kann. Diese Klasse wurde entwickelt, um mit den häufigsten Situationen fertig zu werden, aber Sie scheinen darüber hinaus zu sein. In der Vergangenheit habe ich NSPersistentStoreCoordinator verwendet, um den persistenten Speicher in eine Hintergrundwarteschlange zu laden, und habe dann alle Kontexte für verwaltete Objekte erstellt, die ich brauche. Möglicherweise können Sie damit umgehen, indem Sie zuerst die Datei NSPersistentStoreCoordinator für die Migration verwenden und dann die Datei NSPersistentContainer laden, wenn sie abgeschlossen ist.

Das hängt davon ab, ob Sie den Absturz reproduzieren können. Wenn Sie das jetzt nicht tun können, ist das wahrscheinlich der erste Schritt. Gehen Sie dorthin, wo Sie den gleichen Absturz sehen wie Ihre Benutzer, sodass Sie feststellen können, wann der Absturz behoben wurde.

+0

Ich habe meine Frage aktualisiert, um zu zeigen, wie mein 'NSPersistentContainer' erstellt wird. Das ist alles, was ich benutze, um meinen "Core Data" Stack zu initialisieren, da unsere App iOS 10 ist. Wollen Sie damit sagen, dass ich mein 'NSPersistentContainer' Objekt in einer Hintergrundwarteschlange erstellen könnte? –

+0

Ich kann den Absturz, den meine Benutzer haben, reproduzieren. Ein Benutzer hat mir ihre .sqlite Datei zur Verfügung gestellt. Ich habe den Absturz nie gesehen, seit mein Gerät mit dem Debugger verbunden war und der Watchdog-Prozess nicht läuft. –

+0

@NicHubbard Meine Antwort enthält bereits Details darüber, wie und wann Sie Ihren persistenten Container einrichten sollten. Gibt es etwas Bestimmtes, das nicht klar ist? –

Verwandte Themen