Crash when manipulating a simple Core Data object - iphone

I'm diving into iOS development and I have a few questions about manipulating a simple Core Data object that I created in Xcode. After using the object editor, here's the class that Xcode generated for me...
#interface Alarm : NSManagedObject
{
}
#property (nonatomic, retain) NSNumber * Enabled;
#property (nonatomic, retain) NSString * Label;
#property (nonatomic, retain) NSNumber * Snooze;
#end
#implementation Alarm
#dynamic Enabled;
#dynamic Label;
#dynamic Snooze;
#end
Here's a code snipped where I try and create an Alarm object that I plan to add to my ManagedObjectContext...
- (void)saveAlarm:(id)sender {
Alarm *alarm = [[Alarm alloc] init];
alarm.Label = [NSString stringWithString:txtLabel.text];
alarm.Snooze = [NSNumber numberWithBool:switchSnooze.on];
alarm.Enabled = [NSNumber numberWithBool:YES];
[addAlarmDelegate insertNewAlarm:alarm];
[alarm release];
}
My code crashes the first time I try and assign a value to one of the properties of alarm, on the line...
alarm.Label = [NSString stringWithString:txtLabel.text];
with the following crash message in the console...
reason: '-[Alarm setLabel:]: unrecognized selector sent to instance 0x5e33640
what am I missing here?
Thanks so much in advance for your help!

I would look into using mogenerator:
http://rentzsch.github.com/mogenerator/
The command line to run it is:
mogenerator -m MyAwesomeApp.xcdatamodel -O Classes
Whatever directory you put after -O is where the produced classes go. The great thing is it has simpler methods to create new manage objects in a context, and also produces a class you can customize (adding your own methods) that do not get removed even when you regenerate objects from your model.
Much simpler than using the XCode object generation.

You should not allocate and init an NSManagedObject-based object directly. You should use
[NSEntityDescription insertNewObjectForEntityForName:#"Alarm" inManagedObjectContext:moc];
It might be the reason for it not to work. Because it is usually pretty straight forward to make it work.
The documentation says:
If you instantiate a managed object
directly, you must call the designated
initializer
(initWithEntity:insertIntoManagedObjectContext:).
And in initWithEntity:insertIntoManagedObjectContext:'s documentation:
Important: This method is the
designated initializer for
NSManagedObject. You must not
initialize a managed object simply by
sending it init.

Related

Can I use a custom initializer for a core data model object?

I use Core Data and have an object ExerciseForRoutine. I'm currently manually creating it and then settings it's attributes, which seems to waste code. Is there any way I can create a custom init method to handle this in one line (I know how to do around alloc/init, but core data has a different init method..)
Current Code:
ExerciseForRoutine *exerciseForRoutine = (ExerciseForRoutine *)[NSEntityDescription insertNewObjectForEntityForName:#"ExerciseForRoutine" inManagedObjectContext:managedObjectContext];
exerciseForRoutine.name = self.selectedExercise;
exerciseForRoutine.timeStamp = date;
exerciseForRoutine.muscleGroup = self.muscleName;
exerciseForRoutine.musclePicture = self.muscleURL;
ExerciseForRoutine Class
#class Routine;
#interface ExerciseForRoutine : NSManagedObject {
#private
}
#property (nonatomic, strong) NSDate * timeStamp;
#property (nonatomic, strong) NSString * name;
#property (nonatomic, strong) NSString * muscleGroup;
#property (nonatomic, strong) NSString * musclePicture;
#property (nonatomic, strong) Routine * exerciseToRoutine;
#end
#implementation ExerciseForRoutine
#dynamic timeStamp;
#dynamic name;
#dynamic muscleGroup;
#dynamic musclePicture;
#dynamic exerciseToRoutine;
I did this using awakeFromInsert and awakeFromFetch.
From Apple's documentation:
In a typical Cocoa class, you usually override the designated initializer (often the init method). In a subclass of NSManagedObject, there are three different ways you can customize initialization —by overriding initWithEntity:insertIntoManagedObjectContext:, awakeFromInsert, or awakeFromFetch. You should not override init. You are discouraged from overriding initWithEntity:insertIntoManagedObjectContext: as state changes made in this method may not be properly integrated with undo and redo.
https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/CoreData/Articles/cdManagedObjects.html
The classes which Xcode creates for handling core data objects should not be overridden, instead what you could do is create your own custom class which inherits from NSObject and write your methods to handle the managed object their.
Sol: You can do this with the help of the parameterized init method
Then it would look something like this
CoreDataHelperClass *someobj = [[CoreDataHelperClass alloc]initWithname:#"name" andTimeStamp:#"Time" andMuscleGroup:#"musclegroup" andPicture:UIImagePNGRepresentation(someimageObj)];
To do the above you need to add your own init method in the CoreDataHelperClass class like this
.h part of CoreDataHelperClass
- (id)initWithName:(NSString*)name andTimeStamp:(NSString*)timeStamp andMuscleGroup:(NSString*)group andPicture:(NSData*)imageData;
.m part of CoreDataHelperClass
- (id)initWithName:(NSString*)name andTimeStamp:(NSString*)timeStamp andMuscleGroup:(NSString*)group andPicture:(NSData*)imageData
{
//you assignment code to the core data attributes goes here
ExerciseForRoutine *obj = [[ExerciseForRoutine alloc]init];
obj.name = name;
obj.timestamp = timeStamp;
//and so on
return self;
}
Anyways what you could also do is pass a dictionary with the keyvalue pair get the values in your custom class or you may also pass an NSMutableArray like what ever suits your business model both will work.
You can get the values of Dictionary or Array inside your CoreDataHelperClass and assign those values to your attribute.
Hope i have got your query right if not then kindly mention the error part via comments
To add to #Radix's answer, you should consider using mogenerator because it'll do much of that subclassing business for you.
http://rentzsch.github.io/mogenerator/
See here for a guide to set it up and have it running on XCode 5.
There's a small caveat to watch out for though: if you get an assertion failure that reads:
-[MOGeneratorApp setModel:] blah blah blah
Then you should point mogenerator to the .xcdatamodel file inside of the .xcdatamodeld package in your Run Script Phase, like so:
mogenerator -m Model.xcdatamodeld/Model.xcdatamodel -O Project/Model --template-var arc=true
Where Project is the name of your project and Model is the name of your model.
See https://github.com/rentzsch/mogenerator/issues/169.

iOS error on array initialisation

I'm trying to initialise an array of Photos, and am doing so like this:
NSMutableArray *photoList = [[NSMutableArray alloc] init];
self.photos = photoList;
But when this line of code runs, I get this error:
'NSInvalidArgumentException', reason: '-[Project setPhotos:]: unrecognized selector sent to instance 0xee8e310'
I've spent about three hours trying to find a fix but couldn't, can anybody tell me what to do?
It seems to me that you haven't created a property photos and if you have then it would also seem that this property is not #synthesize'd in your implementation, maybe you are using #dynamic, in which case it is up to you to create a - (void)setPhotos:(NSMutableArray*)photos; method
self does not have a property called photos
You need to add #property (nonatomic, strong) NSArray *photos; in your .h file, before the #end
and in the .m file, #synthesize photo;
This line of code:
self.photos = photoList;
gets turned into this line
[self setPhotos:photoList];
by the compiler - the dot notation is what is called "syntatic sugar" as is just makes it easier to type, it doesn't really shorten the code.
If you have created your own getters and setters (ie)
- (NSMutableArray *)photos;
- (void)setPhotos:(NSMutableArray *)myPhotos
Then you can use that sugar even though you don't have a property called "photos" - although this is considered a misuse of the feature (I show it for comparison purposes).
When you create a property named photos:
#property (nonatomic, strong) NSMutableArray *photos;
the compiler will generate an ivar for you using the same name, but not create the getters and setters. The line:
#synthesize photos;
asks the compiler to do a getter (in all cases) and a setter (if the property is read write). If you do not provide a #synthesize statement, the compiler normally complains, so people should be observing these warnings.
You can see in the error that you have no setPhotos, thus your problem can be fixed quite easily.
Seems like you haven't written a setter method for photos.
in your .h
#property (strong, nonatomic) NSArray * photos;
in your .m (if not using xcode 4.4)
#synthesize photos;
or you could just try writing it like this
photos = [NSArray arrayWithArray:photoList];
For reference if you use [self setMyArray:array] or self.array = myArray
Then you are using the setter method, which is something you probably want to do. If you just point array = myArray, you would be pointing to myArray and if it were released your pointer would point in to the abyss. It's good to not to do that, not using the setter means you are accessing the variable photos directly and this may not be what you want.
Whoops, I'd used #dynamic photos instead of #synthesize photos

Lexical or Preprocessor Issue error causing Core data class "may not respond to" method warnings?

I get Lexical or Preprocessor Issue 'Group.h' file not found which I believe is causing the issues below.
I'm trying to call a method on one of my core data class instances and I get a 'Group' may not respond to '-addPeople:' warning. But I do have an addPeople method in my XCode generated Group class, and here it is:
- (void)addPeople:(NSSet *)value {
[self willChangeValueForKey:#"people" withSetMutation:NSKeyValueUnionSetMutation usingObjects:value];
[[self primitiveValueForKey:#"people"] unionSet:value];
[self didChangeValueForKey:#"people" withSetMutation:NSKeyValueUnionSetMutation usingObjects:value];
}
I also get the same warning if I try to removePeople. Both of these methods have to do with NSSets but I'm able to call my setters from the Group class just fine.
[selectedObject setTitle:[[titleCell textField] text]]; // works
[selectedObject setSubtitle:[[subTitleCell textField] text]]; // works
NSSet *tempPeople = [NSSet setWithArray:people];
[selectedObject addPeople:tempPeople]; // works, but with warning
Side note
When I type [selectedObject I don't get autocompletion, although the word selectedObject does autocomplete as a Group. So at least that's good.
Group.h
#class People;
#interface Group : NSManagedObject {
#private
}
#property (nonatomic, retain) NSString * title;
#property (nonatomic, retain) NSNumber * order;
#property (nonatomic, retain) NSString * subtitle;
#property (nonatomic, retain) NSSet* people;
#end
Updated
I just finished making a new UITableViewCell class and now instead of it saying Lexical or Preprocessor Issue 'Group.h' file not found it now says Lexical or Preprocessor Issue 'PersonCell.h' file not found
It would be helpful if you were able to post your interface file as well. These are a few reasons I might see for this:
You HAVE a Group.h file, but it does not have a method declaration for these methods.
You HAVE a Group.h file, it does have method declarations, but you are not #importing the Group.h into the other class.
You do NOT have a Group.h file at all, and are attempting to configure the method and variables as private objects in the implementation file.
Can you post more of your code for us? Possibly the [selectedObject] declaration as well?
Lexical or Preprocessor Issue
Well, I couldn't figure out what my problem was so I made a new project and remade each view controller, core data and whatever else I needed. And that solved my problem. I wish I knew what was really causing the Lexical error but from the looks of other people's posts, it could have been a bug.
Group' may not respond to '-addPeople:
This issue was resolved by adding the methods to the .h file. I'm not sure why XCode didn't make those for me, but that solved that problem.
And thanks xianritchie for trying to figure this out with me. It was appreciated.

do I need to manually create a NSMutableArray if it is already covered by a property & synthesize method? (code attached)

Do I need to manually create a NSMutableArray if it is already covered by a property & synthesize method?
In the code below I'm seeing an issue whereby the "addEvent" method doesn't seem to be working. Even after calling it the count for the _events variable is still zero (0). I'm wondering in this code whether the issue might be that I need to manually create/initialise the Array? (and not rely on the sythesize method doing this)
Header File
#import <Foundation/Foundation.h>
#interface Weekend : NSObject {
NSMutableArray* _events;
}
- (void)addEvent:(EKEvent*)event;
#property (nonatomic, retain) NSMutableArray* events;
#end
Implementation
#import "Weekend.h"
#implementation Weekend
#synthesize events = _events;
- (void)addEvent:(EKEvent*)event {
[self.events addObject:event];
}
#end
Yes.
The #synthesize automatically creates the getter/setter methods that are used when you refer to self.events. It does not allocate (or release) the _events object for you.
You can create it in the init method, but if you want to get a little fancier, you can also override the getter method like this:
-(NSMutableArray *)events
{
if (_events == nil) {
_events = [[NSMutableArray alloc] init];
}
return _events;
}
If you do it this way instead of in your init method, then your variables only get initilized when they're actually needed, which can be handy in some cases.
Remember that you still need to release in the dealloc method.
you need to allocate the memory first .
The problem is you need to allocate memory to your array ..
_events=[[NSMutableArray alloc]init];
& then add objects to this array.
& also don't forget to release this array at appropriate place , otherwise will create lot of crashing issue.
_events = nil;
[_events release];
If you want to access the getter & setter properties then & then only do
#property (nonatomic, retain) NSMutableArray* events;
& synthesize the array otherwise simply it will work for you.

How do I call NSManaged object's #dynamic method from from another object using a #selector and NSInvocation?

Cocoa newbie here. I am working on an iPhone UITableViewController-based widget that can be used to edit date and text properties in an object set during initiation. Currently, I am attempting to do this with a #selector and NSInvocation as below. Note: the "targetObject" is the object set when the controller is initialized.
- (IBAction)saveDate:(id)sender {
//The selector below would normally be passed in when the controller is initialized
[self setDoneSelector:#selector(setDate:)];
NSMethodSignature * sig = nil;
sig = [[targetObject class] instanceMethodSignatureForSelector:[self doneSelector]];
NSInvocation * myInvocation = nil;
myInvocation = [NSInvocation invocationWithMethodSignature:sig];
[myInvocation setTarget:targetObject];
[myInvocation setSelector:doneSelector];
NSDate * myDate = [datePicker date];
[myInvocation setArgument:&myDate atIndex:2];
NSString * result = nil;
[myInvocation retainArguments];
[myInvocation invoke];
}
This works fine on most objects, but I am running into trouble when passing in a Core Data (NSManagedObject) as the targetObject. The object looks like this:
Transaction.h
#import <CoreData/CoreData.h>
#interface Transaction : NSManagedObject
{
}
#property (nonatomic, retain) NSString * message;
#property (nonatomic, retain) NSDate * date;
#end
Transaction.m
#import "Transaction.h"
#implementation Transaction
#dynamic message;
#dynamic date;
#end
If I set this object in my controller as the targetObject, I can call the "setDate:" method directly without issue.
[targetObject setDate:[datePicker date]];
But when I try to invoke it with the #selector, I get 'Program received signal: "EXC_BAD_ACCESS”.'
I imagine this has something to do with the #dynamic methods used in the NSManagedObject and when they are created, but I don't know enough about that process to know how to or if I can workaround this to get it working. I have tried explicitly creating the "setDate:(NSDate *)aDate" method in the Transaction object, and that works, but I am wondering if I should do that and how it might the NSManagedObject.
Can I access these setter methods with a #selector without explicitly defining them?
Agreed w/ NSD here. You should start by simplifying this code to the much simpler -performSelector:withObject: version:
- (IBAction)saveDate:(id)sender {
[self.targetObject performSelector:self.doneSelector withObject:[self.datePicker date]];
}
If that still has trouble, we can start debugging where the real problem is. NSInvocation is a very fancy object for solving this kind of simple problem.
If you still get the crash, then you'll want to look at the actual stacktrace to see what variable is not being correctly initialized or being over-released.