I have recently released an update to my app in the App Store. The testing brought no crashes but I noticed when it was released in the App Store, that one scenario crashed my app on my 5s. This scenario was not crashing during development. I have received a report from a user saying it also crashed in one scenario on their 5s, which is the same as what I was doing.
I'm at an intermediate stage of iOS development and this is my first experience with diagnosing a crash report.
Update: Please scroll to the bottom of the questions for further updates on the issues
Within Xcode, I have the following crash report for my app on my 5s:
Exception Type: EXC_BAD_ACCESS (SIGSEGV)
Exception Subtype: KERN_INVALID_ADDRESS at 0x0000000000000060
Triggered by Thread: 0
Thread 0 Crashed:
0 CoreData 0x00000001822f8af4 _propertyAtIndexForEntityDescription + 24
1 CoreData 0x00000001822f0330 snapshot_get_value_as_object + 304
2 Foundation 0x0000000183080a78 -[NSObject(NSKeyValueCoding) valueForKeyPath:] + 288
3 Foundation 0x00000001831613cc -[NSSortDescriptor compareObject:toObject:] + 140
4 CoreData 0x0000000182305d9c +[NSFetchedResultsController(PrivateMethods) _insertIndexForObject:inArray:lowIdx:highIdx:sortDescriptors:] + 284
5 CoreData 0x00000001823dd808 -[NSFetchedResultsController(PrivateMethods) _createNewSectionForObject:] + 492
6 CoreData 0x0000000182305b58 -[NSFetchedResultsController(PrivateMethods) _postprocessInsertedObjects:] + 528
7 CoreData 0x00000001823025f8 -[NSFetchedResultsController(PrivateMethods) _managedObjectContextDidChange:] + 2036
8 CoreFoundation 0x000000018259b760 __CFNOTIFICATIONCENTER_IS_CALLING_OUT_TO_AN_OBSERVER__ + 16
9 CoreFoundation 0x00000001824e82b0 _CFXNotificationPost + 2060
10 Foundation 0x000000018307a744 -[NSNotificationCenter postNotificationName:object:userInfo:] + 68
11 CoreData 0x0000000182301dd8 -[NSManagedObjectContext(_NSInternalNotificationHandling) _postObjectsDidChangeNotificationWithUserInfo:] + 84
12 CoreData 0x0000000182301d58 -[NSManagedObjectContext(_NSInternalChangeProcessing) _createAndPostChangeNotification:withDeletions:withUpdates:withRefreshes:] + 360
13 CoreData 0x0000000182300348 -[NSManagedObjectContext(_NSInternalChangeProcessing) _processRecentChanges:] + 2224
14 CoreData 0x00000001822ff264 -[NSManagedObjectContext save:] + 152
15 Lopey 0x000000010004da3c -[LopeyAddEntryViewController save:] (LopeyAddEntryViewController.m:828)
16 UIKit 0x00000001855e50ac -[UIApplication sendAction:to:from:forEvent:] + 96
17 UIKit 0x00000001855e50ac -[UIApplication sendAction:to:from:forEvent:] + 96
18 UIKit 0x00000001855e5040 -[UIApplication sendAction:toTarget:fromSender:forEvent:] + 20
19 UIKit 0x00000001855ce51c -[UIControl _sendActionsForEvents:withEvent:] + 372
20 UIKit 0x00000001855e4a40 -[UIControl touchesEnded:withEvent:] + 580
21 UIKit 0x00000001855e46d4 -[UIWindow _sendTouchesForEvent:] + 688
22 UIKit 0x00000001855df36c -[UIWindow sendEvent:] + 1168
23 UIKit 0x00000001855b0b4c -[UIApplication sendEvent:] + 252
24 UIKit 0x00000001855aec3c _UIApplicationHandleEventQueue + 8496
25 CoreFoundation 0x00000001825a77f0 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 20
26 CoreFoundation 0x00000001825a6b4c __CFRunLoopDoSources0 + 252
27 CoreFoundation 0x00000001825a4de4 __CFRunLoopRun + 628
28 CoreFoundation 0x00000001824e5dcc CFRunLoopRunSpecific + 448
29 GraphicsServices 0x00000001881cdc08 GSEventRunModal + 164
30 UIKit 0x0000000185616fc0 UIApplicationMain + 1152
31 Lopey 0x000000010005a40c main (main.m:16)
32 libdyld.dylib 0x000000018f0e3a9c start + 0
I also have a bit further down:
Thread 0 crashed with ARM Thread State (64-bit):
x0: 0x0000000000000000 x1: 0x0000000000000000 x2: 0x0000000000000000 x3: 0x0000000000000000
x4: 0x0000000000000001 x5: 0x0000000000000074 x6: 0x0000000000000074 x7: 0x0000000000000000
x8: 0x0000000000000060 x9: 0x0000000000000020 x10: 0x0000000100124540 x11: 0x000000100000001f
x12: 0x00000001001245a0 x13: 0x0000000178242d60 x14: 0x0000000000000034 x15: 0x0000000004060401
x16: 0x000000018a1c96e6 x17: 0x000000018234e2d4 x18: 0x0000000000000000 x19: 0x0000000000000000
x20: 0x000000017043b580 x21: 0x0000000000000011 x22: 0x000000017043c1e0 x23: 0x0000000000000006
x24: 0x000000018265d35b x25: 0x000000018f715000 x26: 0x000000018f715000 x27: 0x0000000000000002
x28: 0x00000001780dbd60 fp: 0x000000016fdfc850 lr: 0x00000001822f0334
sp: 0x000000016fdfc840 pc: 0x00000001822f8af4 cpsr: 0x60000000
As can be seen in the logs, I am using Core Data
with NSFetchedResultsController
. I have a UITableViewController
that gets populated as a result of the user clicking a button in the UINavigationBar
, adding some entries into a a few UITextField
s and pressing "Save". That is the point it crashed. However, I have to mention that it doesn't crash every time. In fact, before the crash, I had added in 45 entries consecutively and after it, I added in 100 with no crashes. It has only happened once.
The point in the app where it is crashing is when I press Save (which will dismiss the ViewController
and return back to the UITableView
. With this in mind, it is either the actual "saving" of the NSManagedObjectContext
, or it's the loading of the UITableView. My money is on the former but I'm not really sure.
Here's my code for the NSManagedObjectContext
in the App Delegate
:
- (NSManagedObjectContext *)managedObjectContext
{
if (_managedObjectContext != nil) {
return _managedObjectContext;
}
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (coordinator != nil) {
NSManagedObjectContext* moc = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
moc.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy;
[moc setPersistentStoreCoordinator: coordinator];
_managedObjectContext = moc;
}
return _managedObjectContext;
}
Here's my Save
method in the App Delegate
:
- (void)saveContext
{
NSError *error = nil;
NSManagedObjectContext *managedObjectContext = self.managedObjectContext;
if (managedObjectContext != nil) {
if ([managedObjectContext hasChanges] && ![managedObjectContext save:&error]) {
// Replace this implementation with code to handle the error appropriately.
// abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
//NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
abort();
}
}
}
As mentioned, I'm fairly new to development and this is the first time I've seen a crash log that needs investigating. Any direction in this would really be appreciated. Is it also something relating to the 64-bit processor in the 5s? (Perhaps this would explain why I can't reproduce it elsewhere?).
Update 1
My app is iOS 7 only. My 5s is only iOS 7 and I've tested my app on iOS 8 but on a 4s and experienced no similar crashes. Also, I've tested on an iPad Mini as well, so this is an iPhone 5s only issue.
Update 2
Showing the code that saves the new objects below. It may look complicated but essentially the name, event, etc is based on auto-completion which is based on which objects are already in Core Data, etc.
- (IBAction)save:(id)sender
{
NSManagedObjectContext *context = [self managedObjectContext];
// Insert a new transaction
Transaction *transaction = [NSEntityDescription insertNewObjectForEntityForName:@"Transaction" inManagedObjectContext:context];
Item *itemType = [NSEntityDescription insertNewObjectForEntityForName:@"Item" inManagedObjectContext:context];
itemType.amount = self.itemTextField.text;
transaction.item = itemType;
transaction.wasReceived = @(self.isReceivedSegment.selectedSegmentIndex == 0);
Person *enteredPerson = (Person *)[Person personWithName:self.nameTextField.text inManagedObjectContext:context];
transaction.whoBy = enteredPerson;
Occasion *enteredOccasion = (Occasion *)[Occasion occasionWithTitle:self.occasionTextField.text inManagedObjectContext:context];
transaction.occasion = enteredOccasion;
Subevent *enteredSubevent = (Subevent *)[Subevent subeventWithTitle:self.subeventTextField.text inManagedObjectContext:context];
transaction.subevent = enteredSubevent;
NSCalendar *cal = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
NSDateComponents *components = [cal components:NSYearCalendarUnit|NSMonthCalendarUnit|NSDayCalendarUnit
fromDate:self.datePicker.date];
[components setTimeZone:[NSTimeZone timeZoneWithAbbreviation:@"UTC"]];
NSDate *selectedDate = [cal dateFromComponents:components];
Date *date = (Date *)[Date occasionWithDate:selectedDate inManagedObjectContext:context];
transaction.dates = date;
NSError *error = nil;
if (![context save:&error])
{
// Error
}
[self dismissViewControllerAnimated:YES completion:nil];
}
Within the UITableViewController, my fetchedResultsController is below:
- (NSFetchedResultsController *)fetchedResultsController
{
NSManagedObjectContext *managedObjectContext = [self managedObjectContext];
if (_fetchedResultsController != nil)
{
return _fetchedResultsController;
}
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:@"Transaction" inManagedObjectContext:managedObjectContext];
fetchRequest.entity = entity;
// I am implementing a search here so we can either search by name or event with the prediate filtering out the information.
if ([self.timelineSearchBar.text length] > 0) {
NSPredicate *predName = [NSPredicate predicateWithFormat:@"whoBy.name CONTAINS[c] %@", self.timelineSearchBar.text];
NSPredicate *predOccasion = [NSPredicate predicateWithFormat:@"occasion.title CONTAINS[c] %@", self.timelineSearchBar.text];
// Adding in another predicate for searching by subevent
NSPredicate *predSubOccasion = [NSPredicate predicateWithFormat:@"subevent.title CONTAINS[c] %@", self.timelineSearchBar.text];
// creating the orCompoundPredicate for the two conditions
NSPredicate *compPredicate = [NSCompoundPredicate orPredicateWithSubpredicates:@[predName, predOccasion, predSubOccasion]];
[fetchRequest setPredicate:compPredicate];
}
// Sorting by date and by name.
NSSortDescriptor *sort = [[NSSortDescriptor alloc] initWithKey:@"dates.dateOfEvent" ascending:NO];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"whoBy.name" ascending:YES selector:@selector(localizedCaseInsensitiveCompare:)];
fetchRequest.sortDescriptors = @[sort, sortDescriptor];
fetchRequest.fetchBatchSize = 20;
NSFetchedResultsController *theFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:managedObjectContext sectionNameKeyPath:@"sectionDateFormatter" cacheName:nil];
self.fetchedResultsController = theFetchedResultsController;
_fetchedResultsController.delegate = self;
return _fetchedResultsController;
}
Hope this is helpful!
Update 3
I wanted to try reproduce the issue. The steps to reproduce the first time involved updating from a previous version of the app. I had the App store version on my device before the update, and when the update was released, I updated to that, created a new entry and that's the point it crashed. I ran the old version of my app, and upgraded to this new version in Xcode and when the app was upgraded and ready to go, I added a new entry and with an Exception Breakpoint, it crashed at transaction.dates = date
; in the Save method listed above, in update 2. The breakpoint was reporting: Thread 1: EXC_BAD_ACCESS (code=1,address=0x60)
.
I performed the steps again, without the breakpoint and it stopped at the same line of code with the same error.
Update 4
This crash only occurs if I update my app to the latest version and add a new entry straight away. If however I re-run the app before adding a new entry, the device doesn't crash.
Update 5
With some further testing, I have discovered that this crash is as a result of updating to this new version on an iPhone 5s and an iPad Air only. The app doesn't crash on any other device. With this latest version of the app, I have brought iCloud synchronisations. In the App Delegate
, I have the code below which will get run when the user chooses to select to use iCloud in the tutorial of the update. It is with this scenario that the app crashes.
- (void)initialMigrationOfLocalDataToiCloud
{
NSURL *storeURL = [self.persistentStoreCoordinator.persistentStores.lastObject URL];
NSPersistentStore *currentStore = self.persistentStoreCoordinator.persistentStores.lastObject;
NSURL *cloudURL = [self grabCloudPath:@"CloudLogs"];
NSString *cloudStoreTitle = @"LopeyCloud";
NSDictionary *options = @{NSPersistentStoreUbiquitousContentURLKey: cloudURL,
NSPersistentStoreUbiquitousContentNameKey: cloudStoreTitle, NSMigratePersistentStoresAutomaticallyOption : @YES,
NSInferMappingModelAutomaticallyOption : @YES};
[self.persistentStoreCoordinator migratePersistentStore:currentStore toURL:storeURL options:options withType:NSSQLiteStoreType error:nil];
}
I have the lightweight migration there because the user may be updating from a previous version, etc.
With an exception breakpoint set (and even without it), the app will crash at the transaction.dates = date;
code in the save
method, as per Update 2. I'm not allowed to jump out of the code or do anything with this. Walking through the debugger, I can't see what the value of transaction.dates
is, but date
is not nil. Even if I don't set a breakpoint, the app will crash at the same line of code here.
I hope this makes sense.