I am trying to perform a simple task. I have an array of dictionaries in json format. I want to create an array of objects from it.
+ (NSArray<BuyableUnit*>*)mapUnits:(NSArray<NSDictionary*>*)obj {
NSMutableArray *units = [[NSMutableArray alloc] init];
for (id each in obj) {
BuyableUnit *unit = [self mapUnit:each];
[units addObject:unit];
}
return [units copy];
}
+ (BuyableUnit*)mapUnit:(NSDictionary*)obj {
BuyableUnit *unit = [[BuyableUnit alloc] init];
[unit setType:obj[#"type"]];
[unit setUnit:obj[#"unit"]];
return unit;
}
The error I am getting:
-[BuyableUnit encodeWithCoder:]: unrecognized selector sent to instance 0x60c000293240
2018-06-15 12:51:08.140404-0400 MyApp[20978:20764150] *** -[NSKeyedArchiver dealloc]: warning: NSKeyedArchiver deallocated without having had -finishEncoding called on it.
2018-06-15 12:51:08.152077-0400 MyApp[20978:20764150] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[BuyableUnit encodeWithCoder:]: unrecognized selector sent to instance 0x60c000293240'
*** First throw call stack:
(
0 CoreFoundation 0x00000001119631e6 __exceptionPreprocess + 294
1 libobjc.A.dylib 0x0000000110ff8031 objc_exception_throw + 48
2 CoreFoundation 0x00000001119e4784 -[NSObject(NSObject) doesNotRecognizeSelector:] + 132
3 CoreFoundation 0x00000001118e5898 ___forwarding___ + 1432
4 CoreFoundation 0x00000001118e5278 _CF_forwarding_prep_0 + 120
5 Foundation 0x000000010dbd05f0 _encodeObject + 1197
6 Foundation 0x000000010dbd1af0 -[NSKeyedArchiver _encodeArrayOfObjects:forKey:] + 439
7 Foundation 0x000000010dbd05f0 _encodeObject + 1197
8 Foundation 0x000000010dc02e3a +[NSKeyedArchiver archivedDataWithRootObject:] + 156
9 CoreData 0x000000010cd9d764 -[NSSQLiteConnection execute] + 2084
10 CoreData 0x000000010cef54a1 -[NSSQLiteConnection updateRow:forRequestContext:] + 817
11 CoreData 0x000000010cfbf03b _writeChangesForSaveRequest + 1419
12 CoreData 0x000000010cfc0886 _executeSaveChangesRequest + 390
13 CoreData 0x000000010ce0f812 -[NSSQLSaveChangesRequestContext executeRequestCore:] + 18
14 CoreData 0x000000010cf8f52c -[NSSQLStoreRequestContext executeRequestUsingConnection:] + 204
15 CoreData 0x000000010cf6469b __52-[NSSQLDefaultConnectionManager handleStoreRequest:]_block_invoke + 75
16 libdispatch.dylib 0x0000000112e69848 _dispatch_client_callout + 8
17 libdispatch.dylib 0x0000000112e705b8 _dispatch_queue_barrier_sync_invoke_and_complete + 374
18 CoreData 0x000000010cf64580 -[NSSQLDefaultConnectionManager handleStoreRequest:] + 336
19 CoreData 0x000000010cf6c324 -[NSSQLCoreDispatchManager routeStoreRequest:] + 308
20 CoreData 0x000000010cec2b05 -[NSSQLCore dispatchRequest:withRetries:] + 229
21 CoreData 0x000000010cebecb1 -[NSSQLCore processSaveChanges:forContext:] + 193
22 CoreData 0x000000010cda4373 -[NSSQLCore executeRequest:withContext:error:] + 787
23 CoreData 0x000000010cea568f __65-[NSPersistentStoreCoordinator executeRequest:withContext:error:]_block_invoke + 2015
24 CoreData 0x000000010ce9d7a8 -[NSPersistentStoreCoordinator _routeHeavyweightBlock:] + 360
25 CoreData 0x000000010cda3c02 -[NSPersistentStoreCoordinator executeRequest:withContext:error:] + 642
26 CoreData 0x000000010cdcc69b -[NSManagedObjectContext save:] + 1579
Other Info:
I don't know if this is relevant, but this array of objects is a transformable property of a core data entity.
Question:
How can I have this map the json successfully?
I have tried using BuyableUnit *unit;, but it never initializes so the debugger points to 0x0(nil).
Based on the crash log, I think you need to conform to NSCoding protocol in your BuyableUnit class and implement the following methods:
- (void)encodeWithCoder:(NSCoder *)aCoder {
[aCoder encodeObject:self.type forKey:#"type"];
[aCoder encodeObject:self.unit forKey:#"unit"];
}
- (id)initWithCoder:(NSCoder *)aDecoder {
if (self = [super init]) {
self.type = [aDecoder decodeObjectForKey:#"type"];
self.unit = [aDecoder decodeObjectForKey:#"unit"];
}
return self;
}
Related
I have a Person object with firstName, lastName and category(String).
I would like to create an NSSortDescriptor which allows me to fetch all Person sorted by "category" and after that sorted by firstName lastName.
Example:
The desired order is the follow:
PROSPECT, INATIVE_1Y, GC, VGC.
Then, in the category they must be arranged in alphabetical order (lastName, firstName)
I tried with NSSortDescriptor(key: , ascending: , comparator: ) but without success.
Below my code for the NSFetchRequest
let categoryDescriptor = NSSortDescriptor(key: "category", ascending: true) { (category1, category2) -> ComparisonResult in
let arrayCategory = [PROSPECT, INATIVE_1Y, GC, VGC]
let c1Index = arrayCategory.firstIndex(of: category1) ?? 0
let c2Index = arrayCategory.firstIndex(of: category2) ?? 0
if c1Index < c2Index {
return .orderedAscending
} else if c1Index > c2Index {
return .orderedDescending
}
return .orderedSame
}
objectFetchRequest.sortDescriptors?.append(categoryDescriptor)
let lastNameSortDescriptor = NSSortDescriptor(key: "lastName", ascending: true)
objectFetchRequest.sortDescriptors?.append(lastNameSortDescriptor)
let firstNameSortDescriptor = NSSortDescriptor(key: "firstName", ascending: true)
objectFetchRequest.sortDescriptors?.append(firstNameSortDescriptor)
App crash:
CoreData: error: SQLCore dispatchRequest: exception handling request: <NSSQLFetchRequestContext: 0x600002e82a00> , unsupported NSSortDescriptor (comparator blocks are not supported) with userInfo of (null)
2019-11-05 20:05:59.779136+0200 APP NAME [45129:1204138] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'unsupported NSSortDescriptor (comparator blocks are not supported)'
*** First throw call stack:
(
0 CoreFoundation 0x0000000110efa8db __exceptionPreprocess + 331
1 libobjc.A.dylib 0x000000010f99fac5 objc_exception_throw + 48
2 CoreData 0x00000001109ba53e -[NSSQLGenerator newSQLStatementForRequest:ignoreInheritance:countOnly:nestingLevel:nestIsWhereScoped:requestContext:] + 1646
3 CoreData 0x00000001109c4b0e -[NSSQLiteAdapter _statementForFetchRequestContext:ignoreInheritance:countOnly:nestingLevel:] + 142
4 CoreData 0x00000001108724c4 -[NSSQLiteAdapter newSelectStatementWithFetchRequestContext:ignoreInheritance:] + 116
5 CoreData 0x0000000110a0feb3 -[NSSQLFetchRequestContext _createStatement] + 67
6 CoreData 0x0000000110a0fe4e -[NSSQLFetchRequestContext fetchStatement] + 142
7 CoreData 0x0000000110a10e3b -[NSSQLFetchRequestContext executeRequestCore:] + 27
8 CoreData 0x0000000110a7a790 -[NSSQLStoreRequestContext executeRequestUsingConnection:] + 208
9 CoreData 0x0000000110a4f0eb __52-[NSSQLDefaultConnectionManager handleStoreRequest:]_block_invoke + 75
10 libdispatch.dylib 0x000000011271adb5 _dispatch_client_callout + 8
11 libdispatch.dylib 0x0000000112728d08 _dispatch_lane_barrier_sync_invoke_and_complete + 132
12 CoreData 0x0000000110a4efd0 -[NSSQLDefaultConnectionManager handleStoreRequest:] + 336
13 CoreData 0x0000000110a56c24 -[NSSQLCoreDispatchManager routeStoreRequest:] + 308
14 CoreData 0x000000011099f288 -[NSSQLCore dispatchRequest:withRetries:] + 232
15 CoreData 0x000000011099bffd -[NSSQLCore processFetchRequest:inContext:] + 93
16 CoreData 0x00000001108719ce -[NSSQLCore executeRequest:withContext:error:] + 574
17 CoreData 0x0000000110980657 __65-[NSPersistentStoreCoordinator executeRequest:withContext:error:]_block_invoke + 2039
18 CoreData 0x0000000110978870 -[NSPersistentStoreCoordinator _routeHeavyweightBlock:] + 288
19 CoreData 0x00000001108711a0 -[NSPersistentStoreCoordinator executeRequest:withContext:error:] + 1296
20 CoreData 0x000000011086f475 -[NSManagedObjectContext executeFetchRequest:error:] + 933
21 CoreData 0x0000000110a1f9c0 __43-[NSFetchedResultsController performFetch:]_block_invoke + 448
22 CoreData 0x000000011098d104 gutsOfBlockToNSPersistentStoreCoordinatorPerform + 212
23 libdispatch.dylib 0x000000011271adb5 _dispatch_client_callout + 8
24 libdispatch.dylib 0x0000000112728d08 _dispatch_lane_barrier_sync_invoke_and_complete + 132
25 CoreData 0x0000000110978146 -[NSPersistentStoreCoordinator performBlockAndWait:] + 198
26 CoreData 0x00000001108b2a2a developerSubmittedBlockToNSManagedObjectContextPerform + 170
27 CoreData 0x00000001108b28ff -[NSManagedObjectContext performBlockAndWait:] + 239
28 CoreData 0x0000000110a1fd11 -[NSFetchedResultsController _recursivePerformBlockAndWait:withContext:] + 145
29 CoreData 0x0000000110a1f72b -[NSFetchedResultsController performFetch:] + 299
30 APP NAME 0x0000000109f25278 $s20APP_NAME22GeneralFetchControllerC14refreshResultsyySSF + 1512
31 APP NAME 0x0000000109daca74 $s20APP_NAME19LocalClientSearchVCC14refreshResultsyySSF + 164
32 APP NAME 0x0000000109f24b8b $s20APP_NAME22GeneralFetchControllerC024initializeFetchedResultsG0yyF + 619
33 APP NAME 0x0000000109f247fa $s20APP_NAME22GeneralFetchControllerC11viewDidLoadyyFyycfU_ + 58
34 APP NAME 0x0000000109f2487d $s20APP_NAME22GeneralFetchControllerC11viewDidLoadyyFyycfU_TA + 13
35 APP NAME 0x0000000109c78c4d $sIeg_IeyB_TR + 45
36 libdispatch.dylib 0x0000000112719d7f _dispatch_call_block_and_release + 12
37 libdispatch.dylib 0x000000011271adb5 _dispatch_client_callout + 8
38 libdispatch.dylib 0x0000000112728080 _dispatch_main_queue_callback_4CF + 1540
39 CoreFoundation 0x0000000110e61a79 __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__ + 9
40 CoreFoundation 0x0000000110e5c126 __CFRunLoopRun + 2310
41 CoreFoundation 0x0000000110e5b4d2 CFRunLoopRunSpecific + 626
42 GraphicsServices 0x00000001140b42fe GSEventRunModal + 65
43 UIKitCore 0x000000011a14ffc2 UIApplicationMain + 140
44 APP NAME 0x000000010a307ffb main + 75
45 libdyld.dylib 0x000000011278f541 start + 1
)
libc++abi.dylib: terminating with uncaught exception of type NSException
Without the categorySortDescriptor, the application does not crash.
Thanks for your help
There are (at least) two options:
Add an index attribute for the category order and update it when the category attribute is set.
Add a computed property to return the index from an array in proper order for example a safe version
var categoryIndex : Int {
switch category {
case "PROSPECT": return 0
case "INATIVE_1Y": return 1
case "GC": return 2
case "VGC": return 3
default: fatalError("category not available")
}
}
Then sort by that index.
NSSortDescriptor(key:ascending:comparator: doesn't work because block based API is not supported in SQL request, see the error message
comparator blocks are not supported
My iOS application just crashed with a NSRangeException on -[NSManagedObjectContext save:]. Nothing else helpful anywhere to be found. How am I supposed to fix this? I don't get any memory address or anything that I can work with...
2015-04-22 14:16:38.078 heavenhelp[33559:1734247] *** Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[__NSArrayM objectAtIndex:]: index 6 beyond bounds [0 .. 5]'
*** First throw call stack:
(
0 CoreFoundation 0x0167f746 __exceptionPreprocess + 182
1 libobjc.A.dylib 0x00f40a97 objc_exception_throw + 44
2 CoreFoundation 0x01553b73 -[__NSArrayM objectAtIndex:] + 243
3 CoreData 0x00859cf3 -[NSSQLCore recordToManyChangesForObject:inRow:usingTimestamp:inserted:] + 2531
4 CoreData 0x00856a0b -[NSSQLCore _populateRow:fromObject:timestamp:inserted:] + 2923
5 CoreData 0x00776e24 -[NSSQLCore prepareForSave:] + 1764
6 CoreData 0x00775e3d -[NSSQLCore saveChanges:] + 461
7 CoreData 0x0073f15e -[NSSQLCore executeRequest:withContext:error:] + 638
8 CoreData 0x0083ee75 __65-[NSPersistentStoreCoordinator executeRequest:withContext:error:]_block_invoke + 5349
9 CoreData 0x008492ff gutsOfBlockToNSPersistentStoreCoordinatorPerform + 191
10 libdispatch.dylib 0x035f4bef _dispatch_client_callout + 14
11 libdispatch.dylib 0x035d7b0d _dispatch_barrier_sync_f_invoke + 144
12 libdispatch.dylib 0x035d723f dispatch_barrier_sync_f + 105
13 CoreData 0x008383f7 _perform + 183
14 CoreData 0x0073ec8b -[NSPersistentStoreCoordinator executeRequest:withContext:error:] + 459
15 CoreData 0x0076ee09 -[NSManagedObjectContext save:] + 1529
16 heavenhelp 0x000b6834 _TF10heavenhelp11saveContextFT_T_ + 324
17 heavenhelp 0x0015368d _TFC10heavenhelp26ConversationViewController17viewWillDisappearfS0_FSbT_ + 701
18 heavenhelp 0x001536ef _TToFC10heavenhelp26ConversationViewController17viewWillDisappearfS0_FSbT_ + 63
19 UIKit 0x020a4292 -[UIViewController _setViewAppearState:isAnimating:] + 706
20 UIKit 0x020a4904 -[UIViewController __viewWillDisappear:] + 106
21 UIKit 0x020bcd1d -[UIViewController(UIContainerViewControllerProtectedMethods) beginAppearanceTransition:animated:] + 200
22 UIKit 0x020cafec -[UINavigationController _startCustomTransition:] + 1028
23 UIKit 0x020d8e00 -[UINavigationController _startDeferredTransitionIfNeeded:] + 712
24 UIKit 0x020d9a51 -[UINavigationController __viewWillLayoutSubviews] + 57
25 UIKit 0x02253750 -[UILayoutContainerView layoutSubviews] + 213
26 UIKit 0x01fce57a -[UIView(CALayerDelegate) layoutSublayersOfLayer:] + 668
27 libobjc.A.dylib 0x00f56771 -[NSObject performSelector:withObject:] + 70
28 QuartzCore 0x01d5ee47 -[CALayer layoutSublayers] + 144
29 QuartzCore 0x01d52925 _ZN2CA5Layer16layout_if_neededEPNS_11TransactionE + 403
30 QuartzCore 0x01d5277a _ZN2CA5Layer28layout_and_display_if_neededEPNS_11TransactionE + 26
31 QuartzCore 0x01caec52 _ZN2CA7Context18commit_transactionEPNS_11TransactionE + 284
32 QuartzCore 0x01cb00e5 _ZN2CA11Transaction6commitEv + 487
33 QuartzCore 0x01cb07fc _ZN2CA11Transaction17observer_callbackEP19__CFRunLoopObservermPv + 92
34 CoreFoundation 0x015a086e __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ + 30
35 CoreFoundation 0x015a07b0 __CFRunLoopDoObservers + 400
36 CoreFoundation 0x015961ea __CFRunLoopRun + 1226
37 CoreFoundation 0x01595a5b CFRunLoopRunSpecific + 443
38 CoreFoundation 0x0159588b CFRunLoopRunInMode + 123
39 GraphicsServices 0x046cc2c9 GSEventRunModal + 192
40 GraphicsServices 0x046cc106 GSEventRun + 104
41 UIKit 0x01f3b106 UIApplicationMain + 1526
42 heavenhelp 0x000a5c94 main + 180
43 libdyld.dylib 0x0361fac9 start + 1
)
libc++abi.dylib: terminating with uncaught exception of type NSException
This is the code I used to make my save: thread-safe:
static var onceToken: dispatch_once_t = 0
static var singleton: CoreDataHelper!
static var sharedInstance: CoreDataHelper {
get {
dispatch_once(&onceToken, {
self.singleton = CoreDataHelper()
})
return singleton
}
}
My xcdatamodeld (the relevant part):
Edits: I have edited the code to reflect my changes to make my NSManagedObjectContext thread-safe. Everything I do now, I do on one instance of my CoreDataHelper that I initialise as above. I have found I can trigger a crash by going into one conversation, adding a message, going into another conversation and adding a message there. I have added my xcdatamodeld.
I had apparently set my one-to-many relationship between conversations and messages to "ordered". This was not correct... I had already changed my NSManagedObject's class to use NSMutableSet instead of NSMutableOrderedSet.
Apparently this results in an NSRangeException in the save method...
you said you have made your save-method thread save. can you please show some code?
usually i'm doing something like this:
// create object with concurrency where you need it
[[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
// wrapper for saving
- (BOOL)saveContext
{
__block BOOL success = NO;
NSManagedObjectContext *managedObjectContext = self.managedObjectContext;
if (managedObjectContext != nil)
{
[managedObjectContext performBlockAndWait:^{
if([managedObjectContext hasChanges])
{
NSError *error = nil;
success = [managedObjectContext save:&error];
if (success == NO)
{
NSLog(#"Unresolved error %#", error.localizedDescription);
}
}
}];
}
return success;
}
Somewhere in AppDelegate you are creating NSArray,
By adding Exception Breakpoint you can check the line which causes error.
method to add Exception Breakpoint is on the following links.
http://blog.manbolo.com/2012/01/23/xcode-tips-1-break-on-exceptions
https://developer.apple.com/library/ios/recipes/xcode_help-breakpoint_navigator/articles/adding_an_exception_breakpoint.html
This is my code for loading data from sqlite file
- (void) loadInitialData{
// Form the query.
NSString *query = #"select * from tasklist";
// Get the results.
if (self.toDoItems != nil) {
self.toDoItems = nil;
}
NSArray *dbResultArray = [[NSArray alloc] initWithArray:[self.dbManager loadDataFromDB:query]];
//Logging for debug purpose
NSLog(#"Result set starts");
for(NSArray *subArray in dbResultArray) {
NSLog(#"Data in array: %#",subArray);
}
NSLog(#"Result set ends");
self.toDoItems = [[NSMutableArray alloc]initWithArray:dbResultArray];
// Reload the table view.
//[self.tblPeople reloadData];
[self.tableView reloadData];
}
- (void)viewDidLoad
{
[super viewDidLoad];
self.dbManager = [[DBManager alloc] initWithDatabaseFilename:#"todotaskdb.sql"];
self.toDoItems = [[NSMutableArray alloc] init];
[self loadInitialData];
}
And I am getting this error using NSLog
2014-11-25 18:24:13.862 ToDoList[3213:60b] Result set ends
2014-11-25 18:24:13.865 ToDoList[3213:60b] -[__NSArrayM itemName]: unrecognized selector sent to instance 0x8c996f0
2014-11-25 18:24:13.900 ToDoList[3213:60b] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSArrayM itemName]: unrecognized selector sent to instance 0x8c996f0'
*** First throw call stack:
(
0 CoreFoundation 0x018dc1e4 __exceptionPreprocess + 180
1 libobjc.A.dylib 0x0165b8e5 objc_exception_throw + 44
2 CoreFoundation 0x01979243 -[NSObject(NSObject) doesNotRecognizeSelector:] + 275
3 CoreFoundation 0x018cc50b ___forwarding___ + 1019
4 CoreFoundation 0x018cc0ee _CF_forwarding_prep_0 + 14
5 ToDoList 0x00004882 -[XYZToDoListTableViewController tableView:cellForRowAtIndexPath:] + 306
6 UIKit 0x0041411f -[UITableView _createPreparedCellForGlobalRow:withIndexPath:] + 412
7 UIKit 0x004141f3 -[UITableView _createPreparedCellForGlobalRow:] + 69
8 UIKit 0x003f5ece -[UITableView _updateVisibleCellsNow:] + 2428
9 UIKit 0x0040a6a5 -[UITableView layoutSubviews] + 213
10 UIKit 0x0038a964 -[UIView(CALayerDelegate) layoutSublayersOfLayer:] + 355
11 libobjc.A.dylib 0x0166d82b -[NSObject performSelector:withObject:] + 70
12 QuartzCore 0x03d4745a -[CALayer layoutSublayers] + 148
13 QuartzCore 0x03d3b244 _ZN2CA5Layer16layout_if_neededEPNS_11TransactionE + 380
14 QuartzCore 0x03d473a5 -[CALayer layoutIfNeeded] + 160
15 UIKit 0x0044cae3 -[UIViewController window:setupWithInterfaceOrientation:] + 304
16 UIKit 0x00362aa7 -[UIWindow _setRotatableClient:toOrientation:updateStatusBar:duration:force:isRotating:] + 5212
17 UIKit 0x00361646 -[UIWindow _setRotatableClient:toOrientation:updateStatusBar:duration:force:] + 82
18 UIKit 0x00361518 -[UIWindow _setRotatableViewOrientation:updateStatusBar:duration:force:] + 117
19 UIKit 0x003615a0 -[UIWindow _setRotatableViewOrientation:duration:force:] + 67
20 UIKit 0x0036063a __57-[UIWindow _updateToInterfaceOrientation:duration:force:]_block_invoke + 120
21 UIKit 0x0036059c -[UIWindow _updateToInterfaceOrientation:duration:force:] + 400
22 UIKit 0x003612f3 -[UIWindow setAutorotates:forceUpdateInterfaceOrientation:] + 870
23 UIKit 0x003648e6 -[UIWindow setDelegate:] + 449
24 UIKit 0x0043eb77 -[UIViewController _tryBecomeRootViewControllerInWindow:] + 180
25 UIKit 0x0035a474 -[UIWindow addRootViewControllerViewIfPossible] + 591
26 UIKit 0x0035a5ef -[UIWindow _setHidden:forced:] + 312
27 UIKit 0x0035a86b -[UIWindow _orderFrontWithoutMakingKey] + 49
28 UIKit 0x003653c8 -[UIWindow makeKeyAndVisible] + 65
29 UIKit 0x00315bc0 -[UIApplication _callInitializationDelegatesForURL:payload:suspended:] + 2097
30 UIKit 0x0031a667 -[UIApplication _runWithURL:payload:launchOrientation:statusBarStyle:statusBarHidden:] + 824
31 UIKit 0x0032ef92 -[UIApplication handleEvent:withNewEvent:] + 3517
32 UIKit 0x0032f555 -[UIApplication sendEvent:] + 85
33 UIKit 0x0031c250 _UIApplicationHandleEvent + 683
34 GraphicsServices 0x038d1f02 _PurpleEventCallback + 776
35 GraphicsServices 0x038d1a0d PurpleEventCallback + 46
36 CoreFoundation 0x01857ca5 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__ + 53
37 CoreFoundation 0x018579db __CFRunLoopDoSource1 + 523
38 CoreFoundation 0x0188268c __CFRunLoopRun + 2156
39 CoreFoundation 0x018819d3 CFRunLoopRunSpecific + 467
40 CoreFoundation 0x018817eb CFRunLoopRunInMode + 123
41 UIKit 0x00319d9c -[UIApplication _run] + 840
42 UIKit 0x0031bf9b UIApplicationMain + 1225
43 ToDoList 0x00004e0d main + 141
44 libdyld.dylib 0x01e25701 start + 1
)
libc++abi.dylib: terminating with uncaught exception of type NSException
Ironically, if put below line at the end of viewDidLoad method, then it works.
self.dbManager = [[DBManager alloc] initWithDatabaseFilename:#"todotaskdb.sql"];
Can anyone please tell me what is it that I am doing wrong?
Your toDoItems contains Arrays which contains your items! You're may be getting arrays of the single rows in your database, so you would have to fill them into single toDoItems objects first.
Maybe you could use a factory-like pattern with a method like popToDoItemFromRow:(NSArray *)row which returns your toDoItem for this row.
Then fill an new array in your for (NSArray *subArray in dbResultArray) for these single rows. This new array should you use in you cellForRow...
may be like..
- (void) loadInitialData{
// Form the query.
NSString *query = #"select * from tasklist";
// Get the results.
if (self.toDoItems != nil) {
self.toDoItems = nil;
}
NSArray *dbResultArray = [[NSArray alloc] initWithArray:[self.dbManager loadDataFromDB:query]];
if (self.toDoItems) {
[self.toDoItems removeAllObjects];
}
else {
self.toDoItems = [NSMutableArray new];
}
for(NSArray *subArray in dbResultArray) {
[self.toDoItems addObject:[ToDoItem popToDoItemFromRow:subArray]];
}
// Reload the table view.
//[self.tblPeople reloadData];
[self.tableView reloadData];
}
Crashlytics reported a crash in my app, and I have no idea of what is happening.
Here is the stacktrace:
Thread : Fatal Exception: NSInternalInconsistencyException
0 CoreFoundation 0x30080e83 __exceptionPreprocess + 130
1 libobjc.A.dylib 0x3a3dd6c7 objc_exception_throw + 38
2 CoreData 0x2fdcf941 -[NSSQLiteStatement cachedSQLiteStatement]
3 CoreData 0x2fdcf18d -[NSSQLiteConnection prepareSQLStatement:] + 52
4 CoreData 0x2fde8edf -[NSSQLChannel selectRowsWithCachedStatement:] + 58
5 CoreData 0x2fea8e1f newFetchedRowsForFetchPlan_MT + 910
6 CoreData 0x2fde8949 -[NSSQLCore fetchRowForObjectID:] + 1180
7 CoreData 0x2fde8311 -[NSSQLCore newValuesForObjectWithID:withContext:error:] + 256
8 CoreData 0x2fde762b _PFFaultHandlerLookupRow + 398
9 CoreData 0x2fde7193 _PF_FulfillDeferredFault + 218
10 CoreData 0x2fdf6449 _PF_ManagedObject_WillChangeValueForKeyIndex + 68
11 CoreData 0x2fdf636b _sharedIMPL_setvfk_core + 110
12 CoreData 0x2fe1995d _PF_Handler_Public_SetProperty + 92
13 CoreData 0x2fe1e2a9 -[NSManagedObject setValue:forKey:] + 124
14 Application 0x0004dc45 -[DateFormatter storeValue:error:] (DateFormatter.m:65)
15 Application 0x0004ee17 -[Formatter storeValue:inObject:] (Formatter.m:176)
16 Application 0x0002b5c5 -[NSManagedObject(App) setValuesForKeysWithReceivedDictionary:] (NSManagedObject+App.m:320)
17 Application 0x0002b8f3 -[NSManagedObject(App) setValuesForKeysWithReceivedDictionary:] (NSManagedObject+App.m:377)
18 Application 0x0006352d -[ODataGetOperation processResult:] (ODataGetOperation.m:220)
19 Application 0x0004420b -[ODataOperation connectionDidFinishLoading:] (ODataOperation.m:741)
20 Foundation 0x309bb47f __65-[NSURLConnectionInternal _withConnectionAndDelegate:onlyActive:]_block_invoke + 54
21 Foundation 0x309bb3c1 -[NSURLConnectionInternal _withConnectionAndDelegate:onlyActive:] + 204
22 Foundation 0x309bb2dd -[NSURLConnectionInternal _withActiveConnectionAndDelegate:] + 60
23 CFNetwork 0x2fceaf8f ___ZN27URLConnectionClient_Classic26_delegate_didFinishLoadingEU13block_pointerFvvE_block_invoke + 74
24 CFNetwork 0x2fce9b8f ___ZN27URLConnectionClient_Classic18_withDelegateAsyncEPKcU13block_pointerFvP16_CFURLConnectionPK33CFURLConnectionClientCurrent_VMaxE_block_invoke_2 + 54
25 CFNetwork 0x2fd1b337 ___ZNK17CoreSchedulingSet13_performAsyncEPKcU13block_pointerFvvE_block_invoke + 18
26 CoreFoundation 0x2ffb3ea1 CFArrayApplyFunction + 36
27 CFNetwork 0x2fc81e05 RunloopBlockContext::perform() + 164
28 CFNetwork 0x2fc81cd5 MultiplexerSource::perform() + 220
29 CFNetwork 0x2fc81b65 MultiplexerSource::_perform(void*) + 48
30 CoreFoundation 0x3004bf1f __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 14
31 CoreFoundation 0x3004b3e7 __CFRunLoopDoSources0 + 206
32 CoreFoundation 0x30049bd7 __CFRunLoopRun + 630
33 CoreFoundation 0x2ffb4471 CFRunLoopRunSpecific + 524
34 CoreFoundation 0x2ffb4253 CFRunLoopRunInMode + 106
35 Foundation 0x309a2697 -[NSRunLoop(NSRunLoop) runMode:beforeDate:] + 254
36 Application 0x00043b4b -[ODataOperation send] (ODataOperation.m:548)
37 Application 0x0004277f -[ODataOperation operationWillStartPreparingData] (ODataOperation.m:131)
38 Foundation 0x30a5396d __NSOQSchedule_f + 60
39 libdispatch.dylib 0x3a8c64b7 _dispatch_async_redirect_invoke + 110
40 libdispatch.dylib 0x3a8c77d9 _dispatch_root_queue_drain + 224
41 libdispatch.dylib 0x3a8c79c5 _dispatch_worker_thread2 + 56
42 libsystem_pthread.dylib 0x3a9f1dff _pthread_wqthread + 298
43 libsystem_pthread.dylib 0x3a9f1cc4 start_wqthread + 8
And there is the last method that calls setValue:forKey: in an NSManagedObject subclass
- (BOOL)storeValue:(void *)theValue error:(NSError **)theError
{
NSString * value = (__bridge NSString *)theValue;
NSString * errorMessage = nil;
if (![value length] && !isOptional)
errorMessage = [NSString stringWithFormat: NSLocalizedString(#"Value cannot be nil or empty (Date) for field %#", #""), varName];
else
{
NSDate * date = nil;
if ([value length])
{
date = [NSDate dateWithISO8601: value];
if (!date)
errorMessage = [NSString stringWithFormat: NSLocalizedString(#"Invalid date %# for field %#", #""), theValue, varName];
}
if (IvarAddress)
*((__strong NSDate **) IvarAddress) = date;
else
[currentObject setValue: date forKey: varName];
}
if (errorMessage)
{
if (theError)
*theError = [NSError errorWithDomain: AppDomain code: NSFormattingError userInfo: #{ NSLocalizedDescriptionKey: errorMessage }];
#ifdef DEBUG
NSLog(#"Formatter error: %#", errorMessage);
#endif
return NO;
}
return YES;
}
I am completely open to any suggestion :)
Update : The key exists, and the value being set is an NSDate so it's an attribute of the NSManagedObject, not a relationship.
Update 2 :
I also have another crash with that method (stack trace is not exactly the same) :
Thread : Fatal Exception: NSInternalInconsistencyException
0 CoreFoundation 0x30080e83 __exceptionPreprocess + 130
1 libobjc.A.dylib 0x3a3dd6c7 objc_exception_throw + 38
2 CoreData 0x2fddfacb -[NSSQLCore _obtainOpenChannel] + 234
3 CoreData 0x2fea8ded newFetchedRowsForFetchPlan_MT + 860
4 CoreData 0x2fde8949 -[NSSQLCore fetchRowForObjectID:] + 1180
5 CoreData 0x2fde8311 -[NSSQLCore newValuesForObjectWithID:withContext:error:] + 256
6 CoreData 0x2fde762b _PFFaultHandlerLookupRow + 398
7 CoreData 0x2fde7193 _PF_FulfillDeferredFault + 218
8 CoreData 0x2fdf6449 _PF_ManagedObject_WillChangeValueForKeyIndex + 68
9 CoreData 0x2fdf636b _sharedIMPL_setvfk_core + 110
10 CoreData 0x2fe1995d _PF_Handler_Public_SetProperty + 92
11 CoreData 0x2fe1e2a9 -[NSManagedObject setValue:forKey:] + 124
12 Application 0x000fcc45 -[DateFormatter storeValue:error:] (DateFormatter.m:65)
13 Application 0x000fde17 -[Formatter storeValue:inObject:] (Formatter.m:176)
14 Application 0x000da5c5 -[NSManagedObject(App) setValuesForKeysWithReceivedDictionary:] (NSManagedObject+.m:320)
15 Application 0x000da8f3 -[NSManagedObject(App) setValuesForKeysWithReceivedDictionary:] (NSManagedObject+.m:377)
16 Application 0x0011252d -[ODataGetOperation processResult:] (ODataGetOperation.m:220)
17 Application 0x000f320b -[ODataOperation connectionDidFinishLoading:] (ODataOperation.m:741)
18 Foundation 0x309bb47f __65-[NSURLConnectionInternal _withConnectionAndDelegate:onlyActive:]_block_invoke + 54
19 Foundation 0x309bb3c1 -[NSURLConnectionInternal _withConnectionAndDelegate:onlyActive:] + 204
20 Foundation 0x309bb2dd -[NSURLConnectionInternal _withActiveConnectionAndDelegate:] + 60
The exception message also is different :
_obtainOpenChannel -- NSSQLCore 0x15ef3780: no database channel is available
But I think they are two related issues.
So I finally found the reasons which were all related to multithreading/concurrency issues. Basically updating the same objects on two threads at the same time..
The recently added core data debug flags (-com.apple.CoreData.ConcurrencyDebug 1) were used to figure out what was happening.
Here is the code:
- (void)willTurnIntoFault {
NSManagedObject *relatedObject = self.relatedObject;
if (relatedObject != nil && !relatedObject.isFault && !relatedObject.hasChanges) {
[relatedObject.managedObjectContext refreshObject:relatedObject mergeChanges:NO];
}
[super willTurnIntoFault];
}
Here is the crash:
Application Specific Information:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[_NSObjectID_64_1 hasChanges]: unrecognized selector sent to instance 0x158601a0'
Last Exception Backtrace:
0 CoreFoundation 0x307bfe8b __exceptionPreprocess + 131
1 libobjc.A.dylib 0x3aab96c7 _objc_exception_throw + 39
2 CoreFoundation 0x307c37b7 -[NSObject(NSObject) doesNotRecognizeSelector:] + 203
3 CoreFoundation 0x307c20b7 ___forwarding___ + 707
4 CoreFoundation 0x30710e98 __CF_forwarding_prep_0 + 24
5 Moke 0x0017cd51 -[Status willTurnIntoFault] (Status.m:420)
6 CoreData 0x305356eb -[NSFaultHandler turnObject:intoFaultWithContext:] + 67
7 CoreData 0x305333bb -[NSManagedObject dealloc] + 75
8 CoreData 0x30532b85 -[_PFManagedObjectReferenceQueue _processReferenceQueue:] + 1557
9 CoreData 0x305393c7 -[NSManagedObjectContext(_NSInternalChangeProcessing) _processRecentChanges:] + 3795
10 CoreData 0x30532505 _performRunLoopAction + 317
11 CoreFoundation 0x3078af71 __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ + 21
12 CoreFoundation 0x307888ff __CFRunLoopDoObservers + 287
13 CoreFoundation 0x30788c4b __CFRunLoopRun + 739
14 CoreFoundation 0x306f3541 _CFRunLoopRunSpecific + 525
15 CoreFoundation 0x306f3323 _CFRunLoopRunInMode + 107
16 GraphicsServices 0x3542a2eb _GSEventRunModal + 139
17 UIKit 0x32faa1e5 _UIApplicationMain + 1137
18 Moke 0x000f47e7 main (main.m:16)
19 libdyld.dylib 0x3afb2ab7 start + 3
Here is the self object:
<Status: 0x16a705c0> (entity: Status; id: 0x16a31700 <x-coredata://66EAF889-7CD5-48C2-999E-5E15B84BF8FA/Status/p2235> ; data: {
ID = 3622738251833351;
relatedObject = "0x16a40340 <x-coredata://66EAF889-7CD5-48C2-999E-5E15B84BF8FA/Status/p2242>";
text = "...";
user = "0x16824200 <x-coredata://66EAF889-7CD5-48C2-999E-5E15B84BF8FA/User/p948>";
})
So it seems in this conditional !relatedObject.hasChanges relatedObject becomes _NSObjectID_64_1(which is a subclass of NSManagedObjectID). But how could it be?
PS: If you are curious why it doesn't crash at !relatedObject.isFault, it is because _NSObjectID_64_1 does implement ifFault by my test.
It seems like you are transferring your managed objects between threads. Try to enhance your code this way:
//NSManagedObject *relatedObject = self.relatedObject;
NSManagedObject *relatedObject = [self.managedObjectContext existingObjectWithID:self.relatedObject.objectID error:nil];
Can you call a message as a property ? As far as I know, hasChanges and isFault are messages.
I think it should be :-
if (relatedObject != nil && !([relatedObject isFault]) && !([relatedObject.hasChanges]))