iOS error on array initialisation - iphone

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

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.

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.

Crash when manipulating a simple Core Data object

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.

#property #synthesize

What do #synthesize and #property do in Xcode? Please provide an explanation in really simple terms?
You asked for simple terms:
#property declares a property in your
class header
#property (nonatomic, retain) NSString *myString;
#synthesize creates your setter and
getter for your property (accessor
methods)
Without synthesize you have to write
your own setter and getter
implemention, like getMyString or
setMyString (capitalize the first
character of your property)
Sam: Just an advice: http://www.cocoadevcentral.com/d/learn_objectivec/ is a pretty solid resource to learn about basics like properties.
Good Luck!
Properties and synthesized accessors are new features in Objective-C 2.0.
When you declare a #property you declare somewhat an instance var. Then you #synthesize accessor methods (i.e. getter and setter) for that property.
There are also #dynamic accessors if you're interested.
You should really do your homework on this. Apple has nifty pdf for that.
Think of all objective-c magic as just a "smarter macro", like a "smarter #define statement"
#property if you notice is always in the h file,
#synthesize is always in the m file.
So in the background
#property (whatever) NSString *myString;
becomes a declaration of 2 methods and a private variable;
void set_myString:(NSString *) str;
(NSString*) get_myString;
declarations in the header file
to make them do something their implementation is added into m file when you type in
#synthesize myString;
which becomes something like
void set_myString:(NSString *)str
{
myString = str;
}
(NSString *) get_myString
{
return (myString);
}
But it's smarter than this
depending on if you say "retain" "strong" or "weak"
it will either just return the pointer to the myString or it will copy the myString into a new object
So all of this is done automatically by a compiler just by reading your declarations.
Which is quite useful and saves a lot of time
By default all our variables are Private so we can't acess out of the class.
if we want to use our instance variable in out of the class.
When you declare a #property you declare somewhat an instance var. Then you #synthesize accessor methods (i.e. getter and setter) for that property.
There are also #dynamic accessors if you're interested.
it simply sets the property's setter variable name in it's own class.
For example lets say I have this: #property (nonatomic, copy) NSArray* viewControllers;
Well if i want to access the setter _viewController i wouldn't set a synthesize variable.
but if i want to access the viewController variable by the name viewController instead of _viewController, I would do #synthesize viewController;.
If I wanted to use it as a completely different name I could do this #synthesize viewControllers = viewControlololer; but that's only setter. As you will notice [self viewControllers] only works and not [self viewControlololer];
So I don't understant why everyone writes sets the "setter and getter" of a property. It doesn't change the getter variable at all... unless that means [self viewController] is aware of viewControlololer (obviously).
Actually properties are synthesized, either implicitly or explicitly. Properties are implicitly synthesized. So there's no need to use synthesized unless you wanna change the name of the variable to something different than _property_name.
There are other use cases, for example if you don't want an instance variable to back your property.
Properties are explicitly synthesized by using the #synthesized directive.
(Answer extracted from the Big Nerd Ranch guide)