Crashes in CoreData Batch Update - trotz @try / @catch

    Diese Seite verwendet Cookies. Durch die Nutzung unserer Seite erklären Sie sich damit einverstanden, dass wir Cookies setzen. Weitere Informationen

    Aufgrund der Corona-Krise: Die Veröffentlichung von Stellenangeboten und -gesuchen ist bis 31.12.2020 kostenfrei. Das beinhaltet auch Angebote und Gesuche von und für Freischaffende und Selbstständige.

    • Crashes in CoreData Batch Update - trotz @try / @catch

      Hallo zusammen,

      ich bin etwas ratlos und könnte Hilfe gebrauchen:

      Meine iOS-App nutzt CoreData zur Datenhaltung und führt dabei Batch Updates durch. Bei mir funktionieren diese problemlos, aber iTunes Connect berichtet manchmal von Crashes bei den Anwendern (unter 0,5%): Es sind immer Anweisungen in Apple-internen SQL-Routinen, hauptsächlich -[NSSQLiteConnection prepareSQLStatement:] oder _execute.

      Da ich mich nicht in der Lage sehe, diese Fehler zu vermeiden, habe ich den entsprechenden Aufruf in ein @try / @catch (Objective-C) gepackt. Dieses Vorgehen empfiehlt Apple auch ("The call to executeRequest() can throw an error and therefore requires the try keyword."), leider hatte ich dies anfangs übersehen:

      Quellcode

      1. - (void)cleanupNotifications:(NSTimer *)sender
      2. {
      3. // Reset all subject reminder switches of the past
      4. NSBatchUpdateRequest *updateRequest = [NSBatchUpdateRequest batchUpdateRequestWithEntityName:kSRCoreDataSubjectEntity];
      5. NSPredicate *updatePredicate = [NSPredicate predicateWithFormat:@"subjectHasAlarm == YES AND subjectAlarmDate <= %@", [NSDate date]];
      6. updateRequest.predicate = updatePredicate;
      7. updateRequest.resultType = NSUpdatedObjectIDsResultType;
      8. updateRequest.propertiesToUpdate = @{@"subjectHasAlarm" : @NO};
      9. NSError *error = nil;
      10. NSBatchUpdateResult *updateResult = nil;
      11. @try
      12. {
      13. updateResult = [self.managedObjectContext executeRequest:updateRequest error:&error];
      14. }
      15. @catch (NSException *exception)
      16. {
      17. NSLog(@"NSException caught");
      18. NSLog(@"Name: %@", exception.name);
      19. NSLog(@"Reason: %@", exception.reason);
      20. NSLog(@"Stack Trace: %@", exception.callStackSymbols);
      21. }
      22. @finally
      23. {
      24. if (error) NSLog(@"Error cleaning up reminder switches: %@", error);
      25. // Update all objects in memory as the batch update works only on the persistent store
      26. NSArray *updatedObjects = updateResult.result;
      27. if (updatedObjects.count > 0)
      28. {
      29. [NSManagedObjectContext mergeChangesFromRemoteContextSave:@{NSUpdatedObjectsKey : updatedObjects} intoContexts:@[self.managedObjectContext]];
      30. DebugLog(@"Reset reminder switches (%lu items).", (unsigned long)updatedObjects.count);
      31. // Refresh all lesson views as reminder indicators might have changed
      32. NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
      33. [nc postNotificationName:kSRLessonRefreshNotification object:nil];
      34. }
      35. }
      36. }
      Alles anzeigen
      Obwohl ein entsprechende Update vor Wochen veröffentlich wurde, meldet iTunes Connect weiterhin entsprechende Crashes. Sind das nun nur Anwender, die das Update noch nicht installiert haben, oder übersehe ich etwas Maßgebliches?

      Mattes
      Diese Seite bleibt aus technischen Gründen unbedruckt.
    • MyMattes schrieb:

      ...

      Obwohl ein entsprechende Update vor Wochen veröffentlich wurde, meldet iTunes Connect weiterhin entsprechende Crashes. Sind das nun nur Anwender, die das Update noch nicht installiert haben, oder übersehe ich etwas Maßgebliches?

      Mattes
      Hallo Mattes,

      selber nutze ich iTunes Connect nicht.

      Deinen Sourcecode habe ich kurz überflogen; ein logischer Fehler ist mir nun nicht ins Auge gesprungen. Bin aber auch schon etwas müde.

      Ich gehe mal davon aus, dass Du mit jedem Update die Versionsnummer erhöhst. Wird Dir diese nicht im Report mitgeteilt?
    • OSXDev schrieb:

      Ich gehe mal davon aus, dass Du mit jedem Update die Versionsnummer erhöhst. Wird Dir diese nicht im Report mitgeteilt?
      Damit hast Du natürlich vollkommen recht, ich hatte es bei obiger Überlegung nicht bedacht ... und es war natürlich nicht iTunes Connect gemeint - auch wenn da Crashes gelistet werden - sondern der Organizer in Xcode. Mea culpa.

      Damit ist klar, dass es Crashes der aktuellen App-Version sind ... es bleiben die Fragen, warum und wie ich diese verhindern kann.

      Wenn jemandem eine Lücke in obigem Code auffällt, wäre ich über einen Hinweis dankbar. Andernfalls muss ich damit wohl leben. Angesichts der relativ geringen Anzahlen zu verkraften, aber es nervt mich eben :)

      Mattes
      Diese Seite bleibt aus technischen Gründen unbedruckt.
    • Jetzt mal ganz ins blaue geschossen: Kann es sein, dass die Methode im Background aufgerufen wird bzw. manchmal wenigstens?

      Ich hatte mal so einen ähnlichen Fehler und da ist mir Core Data nicht reproduzierbar immer mal weggeraucht. Es lag dann daran, dass ich einen Background Managed Object Context anstelle des "normalen" Manged Object Context nehmen musste. Warum das nur manchmal zu Problemen führte ist mir bis heute unklar...
    • @AppleDeveloper: Guter Hinweis! Wie man sehen kann, wird die Methode per Timer gefeuert, ich werde nachher mal prüfen, in welchem Thread, das könnte sein.

      Irritierend, dass es nur in sehr seltenen Fällen crasht ... ca. 30 berichtete Fälle bei einigen tausend Benutzern und einer häufig laufenden Routine. Aber wer weiss, was dafür zusammen kommen muss.

      Ich werde berichten, Mattes

      Edit: Nur für mein Verständnis: Müsste das @try / @catch Konstrukt nicht auch die von @AppleDeveloper genannten Abstürze in Core Data abfangen und kontrolliert händeln? Das ist nach meinem Verständnis zumindest der Sinn des Exception Handlings. Insofern mag es gegen das Ursprungsproblem helfen, den Ausführungsmodus zu prüfen (mache ich jetzt), aber das Phänomen der Crashes ist mir damit weiterhin unklar...

      Edit^2: Der Vollständigkeit halber: Die Crashes erfolgten alle im Main Thread...
      Diese Seite bleibt aus technischen Gründen unbedruckt.

      Dieser Beitrag wurde bereits 3 mal editiert, zuletzt von MyMattes ()

    • AppleDeveloper schrieb:

      Aber du schreibst ja, dass das sowohl bei execute als auch prepare Auftritt. Prepare wäre jetzt für mich das was oberhalb von try catch ist. Was ist, wenn das mal mit rein nimmst?
      Nein, das sind Apple-eigene Methoden, die durch executeRequest intern aufgerufen werden. Nach dem Prinzip des Exception-Handlings sollten deren Ausnahmen auch meinen @catch-Block aufrufen und nicht zu einem Programmabbruch führen ... nach meinem Verständnis.

      Mattes
      Diese Seite bleibt aus technischen Gründen unbedruckt.
    • plasmatron schrieb:

      @MyMattes
      Hast du auch in den originalen Crash-Logs nachgeschaut, ob es da einen Application Specific Backtrace gibt (Crash im Organizer > Rechte Maustaste > Show in Finder > Rechte Maustaste > Paketinhalt anzeigen)?
      Sehr guter Hinweis, den kannte ich noch nicht! In den Logs gibt es ein paar unterschiedliche Ursachen, relativ häufig tritt diese auf (Log gekürzt):

      Quellcode

      1. Exception Type: EXC_CRASH (SIGKILL)
      2. Exception Codes: 0x0000000000000000, 0x0000000000000000
      3. Exception Note: EXC_CORPSE_NOTIFY
      4. Termination Reason: Namespace SPRINGBOARD, Code 0xdead10cc
      5. Termination Description: SPRINGBOARD, de.schoolroom-app was task-suspended with locked system files: | /var/mobile/Containers/Shared/AppGroup/C6B88788-AE59-4E6F-8825-7B7004613438/SchoolRoom.sqlite | ProcessVisibility: Foreground Obscured | ProcessState: Suspended
      6. Triggered by Thread: 0
      7. Thread 0 name:
      8. Thread 0 Crashed:
      9. 0 libsystem_platform.dylib 0x000000018fdbf3b4 _platform_strlen + 52
      10. 1 libsqlite3.dylib 0x00000001905b8e94 yy_reduce + 36556 (sqlite3.c:30113)
      11. 2 libsqlite3.dylib 0x00000001905aefcc sqlite3RunParser + 444 (sqlite3.c:152057)
      12. 3 libsqlite3.dylib 0x00000001905ae1c0 sqlite3LockAndPrepare + 1160 (sqlite3.c:127647)
      13. 4 CoreData 0x0000000192c103f0 -[NSSQLiteConnection prepareSQLStatement:] + 472 (NSSQLiteConnection.m:3834)
      14. 5 CoreData 0x0000000192c0deb0 -[NSSQLiteConnection prepareAndExecuteSQLStatement:] + 84 (NSSQLiteConnection.m:3775)
      15. 6 CoreData 0x0000000192e57700 _executeBatchUpdateRequest + 704 (NSSQLCore_Functions.m:891)
      16. 7 CoreData 0x0000000192c29a40 -[NSSQLBatchUpdateRequestContext executeRequestCore:] + 24 (NSSQLBatchUpdateRequestContext.m:84)
      17. 8 CoreData 0x0000000192c058c8 -[NSSQLStoreRequestContext executeRequestUsingConnection:] + 252 (NSSQLStoreRequestContext.m:152)
      18. 9 CoreData 0x0000000192d657fc __52-[NSSQLDefaultConnectionManager handleStoreRequest:]_block_invoke + 84 (NSSQLConnectionManager.m:302)
      19. 10 libdispatch.dylib 0x000000018fbed7d4 _dispatch_client_callout + 16 (object.m:511)
      20. 11 libdispatch.dylib 0x000000018fb9bc1c _dispatch_lane_barrier_sync_invoke_and_complete + 56 (queue.c:989)
      21. 12 CoreData 0x0000000192c05730 -[NSSQLDefaultConnectionManager handleStoreRequest:] + 264 (NSSQLConnectionManager.m:297)
      22. 13 CoreData 0x0000000192c055e4 -[NSSQLCoreDispatchManager routeStoreRequest:] + 264 (NSSQLCoreDispatchManager.m:59)
      23. 14 CoreData 0x0000000192c053a0 -[NSSQLCore dispatchRequest:withRetries:] + 260 (NSSQLCore.m:3418)
      24. 15 CoreData 0x0000000192c2985c -[NSSQLCore processBatchUpdate:inContext:error:] + 120 (NSSQLCore.m:2222)
      25. 16 CoreData 0x0000000192c0640c -[NSSQLCore executeRequest:withContext:error:] + 900 (NSSQLCore.m:2530)
      26. 17 CoreData 0x0000000192d6f6d0 __65-[NSPersistentStoreCoordinator executeRequest:withContext:error:]_block_invoke + 1712 (NSPersistentStoreCoordinator.m:3071)
      27. 18 CoreData 0x0000000192c05190 -[NSPersistentStoreCoordinator _routeHeavyweightBlock:] + 240 (NSPersistentStoreCoordinator.m:593)
      28. 19 CoreData 0x0000000192c05e18 -[NSPersistentStoreCoordinator executeRequest:withContext:error:] + 844 (NSPersistentStoreCoordinator.m:2896)
      29. 20 CoreData 0x0000000192c0da0c -[NSManagedObjectContext executeRequest:error:] + 652 (NSManagedObjectContext.m:1795)
      30. 21 SchoolRoom 0x0000000104cb66d4 -[SRStorageProvider cleanupNotifications:] + 428 (SRStorageProvider.m:186)
      31. 22 Foundation 0x0000000190c366a0 __NSFireTimer + 84 (NSTimer.m:264)
      Alles anzeigen
      Man kann also ziemlich gut sehen, dass mein Timer die oben gequotete Routine triggert und dann x CoreData-Methoden später - in diesem Fall - ein File-Locking der SQLite-Datenbank zur Exception führt. Es könnte evt. sein, dass Core Data nach einer Benutzeraktion den Context speichert und diese Routine dann "gegen die Pumpe läuft" ... ich vermute einfach einmal, dass das Batch-Update intern asynchron läuft, auch wenn der executeRequest-Aufruf im Main-Thread erfolgt (Edit: Quatsch, das Log zeigt ja den gleichen Thread 0).

      Naiv wie ich bin, hätte ich angenommen, dass Core Data in solchen Fällen selber serialisiert. Bleiben für mich zwei Probleme:
      1. Ich habe keine Ahnung, wie ich diese Situation vermeiden sollte (und ob sich der Aufwand angesichts des seltenen Auftretens rechnet)
      2. Ich bin immer noch der Ansicht, das @try / @catch müsste in diesem Fall einen Crash vermeiden
      Irgendwelche Ideen?

      Mattes
      Diese Seite bleibt aus technischen Gründen unbedruckt.
    • Das magische Wort oben scheint "suspended" zu sein: Scheinbar läuft das Batch Update manchmal, wenn die App in Suspension wechselt und damit fliegt die App wegen einer noch gelockten Datei weg. Zumindest hat mein Google-fu gerade in diese Richtung gezeigt ... Ich werde das in der nächsten Zeit mal verfolgen und vielleicht das Update in einen Hintergrund-Prozess (evt. mit gesonderer Zeit beim Suspend) verlagern.

      Mattes
      Diese Seite bleibt aus technischen Gründen unbedruckt.