EXC_BAD_ACCESS while working with Core Data - macos

I'm new into Cocoa and am writing a simple app to learn working with Core Data, but it crashes with EXC_BAD_ACCESS. Tried several things and haven't find the solution yet. As I said, I'm not very experienced in Cocoa.
I have followed the usual Core Data tutorials.
This is my Model:
I've added these two entities as NSArrayController in my Nib file and have two NSTableViews with Value Binding to the entity objects.
And here's the code:
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
NSManagedObjectContext *context = [self managedObjectContext];
TaskList *list = [NSEntityDescription
insertNewObjectForEntityForName:#"TaskList"
inManagedObjectContext: context]; // EXC_BAD_ACCESS happens here
[list setTitle:#"Inbox"];
Task *task = [NSEntityDescription
insertNewObjectForEntityForName:#"Task"
inManagedObjectContext: context];
[task setKey:#"Remember the milk"];
[task setList:list];
NSError *error;
if (![context save:&error]) {
NSLog(#"Error: %#", [error localizedDescription]);
}
}
That's it! That's all my program. I am using Xcode 4.2, developing a Mac app, and ARC is enabled.
UPDATE: jrturton asked me to include implementation of [self managedObjectContext]. I didn't write this code, but here's what I found in AppDelegate.h:
#property (readonly, strong, nonatomic) NSManagedObjectContext *managedObjectContext;
And this is from AppDelegate.m:
#synthesize managedObjectContext = __managedObjectContext;
...
/**
Returns the managed object context for the application (which is already
bound to the persistent store coordinator for the application.)
*/
- (NSManagedObjectContext *)managedObjectContext {
if (__managedObjectContext) {
return __managedObjectContext;
}
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (!coordinator) {
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
[dict setValue:#"Failed to initialize the store" forKey:NSLocalizedDescriptionKey];
[dict setValue:#"There was an error building up the data file." forKey:NSLocalizedFailureReasonErrorKey];
NSError *error = [NSError errorWithDomain:#"YOUR_ERROR_DOMAIN" code:9999 userInfo:dict];
[[NSApplication sharedApplication] presentError:error];
return nil;
}
__managedObjectContext = [[NSManagedObjectContext alloc] init];
[__managedObjectContext setPersistentStoreCoordinator:coordinator];
return __managedObjectContext;
}

Check your managed object model. Make sure all the entity and attribute names are spelled correctly. Also check your object class files and make sure they contain what you expect.
Maybe the debugger does not show you the correct row when crashing: I noticed, that you have a method setKey:, but no attribute called keyin your Task entity. Try setting all the attributes with the dot notation, like list.title = #"Inbox". (This is generally easier to read and avoids similar errors.)
As suggested, before the line inserting the new entity, set a breakpoint and make sure the managed object context is not null.
Finally, perhaps you have to cast your object. insertNewObjectForEntityForName: returns an object of type NSManagedObject, but you are assigning it to a type TaskList. Try adding the cast before you use the object:
TaskList *list = (TaksList *) [NSEntityDescription
insertNewObjectForEntityForName:#"TaskList"
inManagedObjectContext: context];

I had this same issue. I resolved it like Mostafa said above. If you create a project with Core Data enabled, it will automatically create a file for you. Use this .xcdatamodeld file instead of a custom one. If you have one already created, just delete the originally created file and rename your datamodel file to the originally created file name.

Related

post an object to ASP.NET Web API using AFNetworking

the question is simple but I saw the implementation is fairly awkward!!
I want to post an object e.g. Device object to web api web service
// Initialize Client
AFHTTPClient *client = [[AFHTTPClient alloc] initWithBaseURL:[NSURL URLWithString:#"http://rezawebapi.com"]];
//Indicationg this device is online and sending its dEVICE token to the server
Device *device = [Device new];
device.DeviceToken = [[NSUserDefaults standardUserDefaults] objectForKey:#"devicetoken"];
device.IsOnline = #"True";
//updating current active users of this app in the server
NSDictionary *dictionary = [[NSDictionary alloc]initWithObjectsAndKeys:
device.DeviceToken,#"DeviceToken",
device.IsOnline,#"IsOnline",
nil];
client.parameterEncoding = AFJSONParameterEncoding;
[client postPath:#"/api/iosAppstats" parameters:dictionary success:^(AFHTTPRequestOperation *operation, id responseObject)
{
NSLog(#"%#", responseObject);
// it crashes on the next line because responseObject is NSData
}failure:^(AFHTTPRequestOperation *operation, NSError *error)
{
NSLog(#"%#", error.localizedDescription);
}];
1- is there anyway to send the object without creating an dictionary ?(it is error prone!)
2- when my deviceToken is null the object which it sends to the server is null. but consider here one property deviceToken is null but other properties have their own values! does anyone have any idea?
3- I have defined #property (assign, nonatomic) BOOL IsOnline; but when It creates the dictionary EXEX-BAD-ACCESS rises! how should I define bool value? (I had to define it as NSString. it is not an approved way)
1.
is there anyway to send the object without creating an dictionary ?(it is error prone!)
Your API takes JSON. JSON is just dictionaries, arrays, strings, and numbers. So, no. However, it is not error-prone. Just make sure to only put JSON-compliant objects in your dictionary. Read the NSJSONSerialization Overview for more info.
2.
when my deviceToken is null the object which it sends to the server is null. but consider here one property deviceToken is null but other properties have their own values! does anyone have any idea?
You could add deviceToken conditionally, like so:
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
NSString *deviceToken = [[NSUserDefaults standardUserDefaults] objectForKey:#"devicetoken"];
if (deviceToken) {
[dictionary setObject:deviceToken forKey:#"DeviceToken"];
}
3.
I have defined #property (assign, nonatomic) BOOL IsOnline; but when It creates the dictionary EXEX-BAD-ACCESS rises! how should I define bool value? (I had to define it as NSString. it is not an approved way)
Using a BOOL violates this rule from the NSJSONSerialization overview I linked to in #1:
All objects are instances of NSString, NSNumber, NSArray, NSDictionary, or NSNull.
So if your property is a BOOL or other simple numerical type, wrap it in #() to make it an NSNumber:
[dictionary setObject:#(device.IsOnline) forKey:#"DeviceToken"];
This is the same as:
NSNumber *isOnlineNum = [NSNumber numberWithBool:device.isOnline];
[dictionary setObject:isOnlineNum forKey:#"DeviceToken"];

NSPopupButton in view based NSTableView: getting bindings to work

Problem Description
I'm trying to achieve something that should be simple and fairly common: having a bindings populated NSPopupButton inside bindings populated NSTableView. Apple describes this for a cell based table in the their documentation Implementing To-One Relationships Using Pop-Up Menus and it looks like this:
I can't get this to work for a view based table. The "Author" popup won't populate itself no matter what I do.
I have two array controllers, one for the items in the table (Items) and one for the authors (Authors), both associated with the respective entities in my core data model. I bind the NSManagedPopup in my cell as follows in interface builder:
Content -> Authors (Controller Key: arrangedObjects)
Content Values -> Authors (Controller Key: arrangedObjects, Model Key Path: name)
Selected Object -> Table Cell View (Model Key Path: objectValue.author
If I place the popup somewhere outside the table it works fine (except for the selection obviously), so I guess the binding setup should be ok.
Things I Have Already Tried
Someone suggested a workaround using an IBOutlet property to the Authors array controller but this doesn't seem to work for me either.
In another SO question it was suggested to subclass NSTableCellView and establish the required connections programmatically. I tried this but had only limited success.
If I setup the bindings as follows:
- (NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row {
NSView *view = [tableView makeViewWithIdentifier:tableColumn.identifier owner:self];
if ([tableColumn.identifier isEqualToString:#"Author") {
AuthorSelectorCell *authorSelectorCell = (AuthorSelectorCell *)view;
[authorSelectorCell.popupButton bind:NSContentBinding toObject:self.authors withKeyPath:#"arrangedObjects" options:nil];
[authorSelectorCell.popupButton bind:NSContentValuesBinding toObject:self.authors withKeyPath:#"arrangedObjects.name" options:nil];
[authorSelectorCell.popupButton bind:NSSelectedObjectBinding toObject:view withKeyPath:#"objectValue.author" options:nil];
}
return view;
}
the popup does show the list of possible authors but the current selection always shows as "No Value". If I add
[authorSelectorCell.popupButton bind:NSSelectedValueBinding toObject:view withKeyPath:#"objectValue.author.name" options:nil];
the current selection is completely empty. The only way to make the current selection show up is by setting
[authorSelectorCell.popupButton bind:NSSelectedObjectBinding toObject:view withKeyPath:#"objectValue.author.name" options:nil];
which will break as soon as I select a different author since it will try to assign an NSString* to an Author* property.
Any Ideas?
I had the same problem. I've put a sample project showing this is possible on Github.
Someone suggested a workaround using an IBOutlet property to the Authors
array controller but this doesn't seem to work for me either.
This is the approach that did work for me, and that is demonstrated in the sample project. The missing bit of the puzzle is that that IBOutlet to the array controller needs to be in the class that provides the TableView's delegate.
Had the same problem and found this workaround - basically get your authors array controller out of nib with a IBOutlet and bind to it via file owner.
You can try this FOUR + 1 settings for NSPopUpbutton:
In my example, "allPersons" is equivalent to your "Authors".
I have allPersons available as a property (NSArray*) in File's owner.
Additionally, I bound the tableView delegate to File's owner. If this is not bound, I just get a default list :Item1, Item2, Item3
I always prefer the programmatic approach. Create a category on NSTableCellView:
+(instancetype)tableCellPopUpButton:(NSPopUpButton **)popUpButton
identifier:(NSString *)identifier
arrayController:(id)arrayController
relationship:(NSString *)relationshipName
relationshipArrayController:(NSArrayController *)relationshipArrayController
relationshipAttribute:(NSString *)relationshipAttribute
relationshipAttributeIsScalar:(BOOL)relationshipAttributeIsScalar
valueTransformers:(NSDictionary *)valueTransformers
{
NSTableCellView *newInstance = [[self alloc] init];
newInstance.identifier = identifier;
NSPopUpButton *aPopUpButton = [[NSPopUpButton alloc] init];
aPopUpButton.autoresizingMask = NSViewWidthSizable | NSViewHeightSizable;
[aPopUpButton bind:NSContentBinding //the collection of objects in the pop-up
toObject:relationshipArrayController
withKeyPath:#"arrangedObjects"
options:nil];
NSMutableDictionary *contentBindingOptions = [NSMutableDictionary dictionaryWithDictionary:[[TBBindingOptions class] contentBindingOptionsWithRelationshipName:relationshipName]];
NSValueTransformer *aTransformer = [valueTransformers objectForKey:NSValueTransformerNameBindingOption];
if (aTransformer) {
[contentBindingOptions setObject:aTransformer forKey:NSValueTransformerNameBindingOption];
}
[aPopUpButton bind:NSContentValuesBinding // the labels of the objects in the pop-up
toObject:relationshipArrayController
withKeyPath:[NSString stringWithFormat:#"arrangedObjects.%#", relationshipAttribute]
options:[self contentBindingOptionsWithRelationshipName:relationshipName]];
NSMutableDictionary *valueBindingOptions = [NSMutableDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:YES], NSAllowsEditingMultipleValuesSelectionBindingOption,
[NSNumber numberWithBool:YES], NSConditionallySetsEditableBindingOption,
[NSNumber numberWithBool:YES], NSCreatesSortDescriptorBindingOption,
[NSNumber numberWithBool:YES], NSRaisesForNotApplicableKeysBindingOption,
[NSNumber numberWithBool:YES], NSValidatesImmediatelyBindingOption,
nil];;
#try {
// The object that the pop-up should use as the selected item
if (relationshipAttributeIsScalar) {
[aPopUpButton bind:NSSelectedValueBinding
toObject:newInstance
withKeyPath:[NSString stringWithFormat:#"objectValue.%#", relationshipName]
options:valueBindingOptions];
} else {
[aPopUpButton bind:NSSelectedObjectBinding
toObject:newInstance
withKeyPath:[NSString stringWithFormat:#"objectValue.%#", relationshipName]
options:valueBindingOptions];
}
}
#catch (NSException *exception) {
//NSLog(#"%# %# %#", [self class], NSStringFromSelector(_cmd), exception);
}
#finally {
[newInstance addSubview:aPopUpButton];
if (popUpButton != NULL) *popUpButton = aPopUpButton;
}
return newInstance;
}
+ (NSDictionary *)contentBindingOptionsWithRelationshipName:(NSString *)relationshipNameOrEmptyString
{
NSString *nullPlaceholder;
if([relationshipNameOrEmptyString isEqualToString:#""])
nullPlaceholder = NSLocalizedString(#"(No value)", nil);
else {
NSString *formattedPlaceholder = [NSString stringWithFormat:#"(No %#)", relationshipNameOrEmptyString];
nullPlaceholder = NSLocalizedString(formattedPlaceholder,
nil);
}
return [NSDictionary dictionaryWithObjectsAndKeys:
nullPlaceholder, NSNullPlaceholderBindingOption,
[NSNumber numberWithBool:YES], NSInsertsNullPlaceholderBindingOption,
[NSNumber numberWithBool:YES], NSRaisesForNotApplicableKeysBindingOption,
nil];
}

NSMutableDictionary setObject:forKey - custom class

I am using this in a UINavigation environment.
I have customClassA. It inherits customClassB and one of its object is a NSMutableDictionary.
I alloc and init customClassA in a viewController, then for adding data, I am pushing a new viewController into the stack. The addNewDataViewController sends the newly added data, a customClassB object back by its delegate. Everything works fine so far.
customClassA has to store the returned object (customClassB) into its NSMutableDictionary object with a key (an NSString created from NSDate).
I get "mutating method sent to immutable object" error and can't think of any solution.
Any help is appreciated.
Thanks.
===========================
interface customClassA : NSObject
{
NSDate date;
NSArray *array; // will contain only NSString objects
}
// and the rest as customary
...
#import "customClassA.h"
interface customClassB : NSObject
{
NSString *title;
NSMutableDictionary *data; // will contain values of customClassA with keys of NSString
}
// and the rest as customary
...
#import "customClassB"
#interface firstViewController : UITableViewController <SecondViewControllerDelegate>
- (void)viewDidLoad
{
self.customClassB_Object = [customClassB alloc] init];
// and the rest...
}
- (void)secondViewControllerDidSaveData:(customClassA *)aData
{
[self.customClassB_Object.data setObject:aData forKey:[NSString stringWithFormat:#"%#", aData.date]];
// update tableView
}
Make sure you are initializing the NSMutableDictionary with something like
NSMutableDictionary *myDictionary = [[NSMutableDictionary alloc] init];
It would appear that your NSMutableDictionary is getting created with an NSDictionary instance instead of a NSMutableDictionary
Althoguh I added the following code to customClassB implementation, it still didn't work.
#implementation customClassB
- (id)init
{
self = [super init];
if (self)
self.data = [NSMutableDictionary alloc] init];
return self;
}
so I added two custom methods to my customClassB implementation, as well as in the header file:
- (void)appendData:(customClassA *)aData;
- (void)removeDataWithKey:(NSString *)aKey;
and instead of manipulating the data dicionary of customClassB in my viewController, I simply call that method and pass the data object to the class and it did the trick.

A method for 'Save As...' in Core Data app

CD's been an enormous learning curve for me and there's still a bit for me to go, but any help on the following could enable me to lift the current weight on my shoulders!
I'm trying to write a method that implements a "Save As.." for the user in my CD app.
So far I've got:
[saveAsPanel beginSheetModalForWindow:window completionHandler:^(NSInteger userResult)
{
if (userResult == NSOKButton) {
NSPersistentStoreCoordinator *psc = [self persistentStoreCoordinator];
NSURL *oldURL = [self URLOfInternalStore]; //returns the current store's URL
NSURL *newURL = [saveAsPanel URL];
NSError *error = nil;
NSPersistentStore *oldStore = [psc persistentStoreForURL:oldURL];
NSPersistentStore *sqLiteStore = [psc migratePersistentStore:oldStore
toURL:newURL
options:nil
withType:NSXMLStoreType
error:&error];
}
}];
Unfortunately, I just get the error:
Object's persistent store is not reachable from this NSManagedObjectContext's coordinator.
Should I 'remove' and then 'addPersistentStore...' to update it to the new URL? The doc's seem to suggest that all will be handled with in the 'migrate' method.
Thanks in advance!
Edit:
Ok, well, I've come up with my own 'dirty' method. I can imagine that this isn't an approved way of doing things, but there's no error thrown up and the app works as expected at all times (not often I can say that, either!):
-(IBAction)saveAsAction:(id)sender
{
NSSavePanel *saveAsPanel = [NSSavePanel savePanel];
[saveAsPanel beginSheetModalForWindow:window completionHandler:^(NSInteger userResult)
{
if (userResult == NSOKButton) {
[self saveAction:#"saveAsCalling"];
NSURL *newURL = [saveAsPanel URL];
NSError *error = nil;
[[NSFileManager defaultManager] copyItemAtURL:[NSURL fileURLWithPath:internalStore] toURL:newURL error:&error];
//internalStore is a hard-wired NSString that holds the path to the bundle's database
}
}];
}
-(IBAction)loadAction:(id)sender
{
NSOpenPanel *loadPanel = [NSOpenPanel openPanel];
[loadPanel beginSheetModalForWindow:window completionHandler:^(NSInteger userResult)
{
if (userResult == NSOKButton) {
[self saveAction:#"loadCalling"];
NSURL *newURL = [loadPanel URL];
NSURL *oldURL = [NSURL fileURLWithPath:internalStore];
NSError *error = nil;
NSPersistentStoreCoordinator *psc = [SELF_MOC persistentStoreCoordinator];
[psc removePersistentStore:[[self persistentStoreCoordinator] persistentStoreForURL:oldURL] error:&error];
[psc addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:newURL options:nil error:&error];
[[NSFileManager defaultManager] removeItemAtURL:oldURL error:&error];
[[NSFileManager defaultManager] copyItemAtURL:newURL toURL:oldURL error:&error];
[psc removePersistentStore:[[self persistentStoreCoordinator] persistentStoreForURL:newURL] error:&error];
[psc addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:oldURL options:nil error:&error];
}
}];
}
The basic reasoning is this: to do a 'SaveAs...' I simply copy out the SQLLite store file in the mainBundle to wherever the user selects and rename it to what they want - as per TechZen's suggestion.
To do a 'Load' then I first removePersistentStore from the bundle's file, add the one that the user's just chosen. Delete the bundle store (which in theory isn't now being used) and then copy the user's choice back into the bundle. Finally, the two operations of remove and addPersistentStore are performed to point the app back to it's bundle's file which is now the user's choice.
Hope that makes sense. If anyone has any thoughts on just how unprofessional a methodology this is then please - be kind as I'm fairly new - let me know. I can't find anything that is more elegant.
I know Apple don't like you using removePersistentStore and addPersistentStore but, as I say no errors are reported (in my actual code I scattered NSLog lines throughout to report what error is holding).
You only use a SaveAs... in a document based app. If you use Core Data as your model, you need to use NSPersistentDocument to save your data. It provide the SaveAs... functionality you seek.
Straight Core Data is used for more database-like apps in which the entire app operates from one data set (more or less.)

Core Data fetchedResultsController errors 'A fetch request must have an entity' entityForName returns nil

Hi I set up my own coredata app, or I tried...
First I created the xdatamodel and generated the Modelclasses, after this I implemented all the function of core-data in AppDelegate which I found in a generated project. Finally I copied the fetchedResultsController in my TableViewController.
fetchedResultsController
- (NSFetchedResultsController *)fetchedResultsController {
if (fetchedResultsController_ != nil) {
return fetchedResultsController_;
}
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"ParameterGroup" inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
[fetchRequest setFetchBatchSize:20];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"name" ascending:NO];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:nil cacheName:#"Root"];
aFetchedResultsController.delegate = self;
self.fetchedResultsController = aFetchedResultsController;
[aFetchedResultsController release];
[fetchRequest release];
[sortDescriptor release];
[sortDescriptors release];
NSError *error = nil;
if (![fetchedResultsController_ performFetch:&error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
return fetchedResultsController_;
}
First I checked if the managedObjectsController is != nil, it has a address
Then I copied the EntityName from my xdatamodel in entityForName,
but NSEntityDescricption entity is nil.
And if I just create a new object the exception says, that the entity doesn't exist
Do I have to connect the xdatamodel to my project?
Hope you can help me
Thanks a lot!!!
The most common cause of this problem is simply misspelling the entity name wrong in the code such that it doesn't match the entity name in the data model.
Copy and paste the entity name from the model to the code and see if that fixes the problem.
The simplest way to solve this, given that you haven't done a lot coding on non-core-data parts, is probably to create a new project where you check the box for "Use Core Data". If you're going to use a Navigation Bar, choose this as your template. If I recall correctly, this will generate a table view with all functions needed. You'll have to modify the datamodel (generated).
Remark that you'll have to delete the app from the Simulator if it is installed and you change the datamodel (otherwise the generated data will not be consistent with the datamodel and the app will crash)

Resources