I'm new to Objective C and was wondering if anyone can help me.
I am using core data with a sqlite database to hold simple profile objects which have a name and a score attribute (both of which are of type NSString).
What I want to do is fetch the profiles and store them in an NSData object, please see my code below:
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"GamerProfile" inManagedObjectContext:managedObjectContext];
[fetchRequest setEntity:entity];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"Name" ascending:YES];
NSArray *sortDecriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
[fetchRequest setSortDescriptors:sortDecriptors];
[sortDescriptor release];
[sortDecriptors release];
NSError *error;
NSArray *items = [self.managedObjectContext executeFetchRequest:fetchRequest error:&error];
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:items];
[self SendData:data];
[fetchRequest release];
When I run the code I'm getting the error "Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[GamerProfile encodeWithCoder:]: unrecognized selector sent to instance 0x3f4b530'"
I presume I have to add an encodeWithCoderClass to my core data NSManagedObject object (GamerProfile) but I'm not sure how to do this even after reading the documentation, My attempt at doing this is below. I'm not sure if I'm going along the right lines with this as get a warning stating "NSManagedObject" may not respond to '-encodeWithCoder'"
I would really, really appreciate it if someone could point me in the right direction!!
Thanks
C
Here is the code for my GamerProfile (CoreData NSManagedObject Object) with my attempt at adding an encodeWithCoder method...
Header File
#import <CoreData/CoreData.h>
#interface GamerProfile : NSManagedObject <NSCoding>
{
}
#property (nonatomic, retain) NSString * GamerScore;
#property (nonatomic, retain) NSString * Name;
- (void)encodeWithCoder:(NSCoder *)coder;
#end
Code File
#import "GamerProfile.h"
#implementation GamerProfile
#dynamic GamerScore;
#dynamic Name;
- (void)encodeWithCoder:(NSCoder *)coder {
[super encodeWithCoder:coder];
[coder encodeObject:GamerScore forKey:#"GamerScore"];
[coder encodeObject:Name forKey:#"Name"];
}
I got this to work. Here's how.
First create an NSValueTransformer like so:
ArrayToDataTransformer.h
#interface ArrayToDataTransformer : NSValueTransformer {
}
#end
ArrayToDataTransformer.m
import "ArrayToDataTransformer.h"
#implementation ArrayToDataTransformer
+ (BOOL)allowsReverseTransformation {
return YES;
}
+ (Class)transformedValueClass {
return [NSData class];
}
- (id)transformedValue:(id)value {
//Take an NSArray archive to NSData
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:value];
return data;
}
- (id)reverseTransformedValue:(id)value {
//Take NSData unarchive to NSArray
NSArray *array = (NSArray*)[NSKeyedUnarchiver unarchiveObjectWithData:value];
return array;
}
#end
The above is your interface to NSManagedObject, now create one that use it, for example:
Array.h
#import <CoreData/CoreData.h>
#class Arrays;
#interface Array : NSManagedObject
{
}
#property (nonatomic, retain) id myArray;
#property (nonatomic, retain) Arrays * arrayOfArrays;
#end
Array.m
#import "Array.h"
#import "Arrays.h"
#implementation Array
#dynamic myArray;
#dynamic arrayOfArrays;
#end
In the xcdatamodel, Array needs myArray Attributes set as Optional (usually always checked), and Type is: Transformable, and Value Transformer Name: ArrayToDataTransformer
Now you can use it;
NSMutableArray* positionArray;
positionArray = [[NSMutableArray alloc] arrayWithCapacity:[myArray count]];
for(NSArray *pos in myArray) {
[positionArray addObject:[NSString stringWithFormat:#"%#",pos]];
}
NSLog(#"ArrayCtrl : positionArray cnt = %d",[positionArray count]);
//Now add the positionArray to CoreData using the setValue & myArray Key
Array *array = (Array*)[NSEntityDescription insertNewObjectForEntityForName:#"Array" inManagedObjectContext:self.managedObjectContext];
[array setValue:positionArray forKey:#"myArray"];
[myArrays setMyArrays:array];
[self saveAction:array];
[positionArray release];
To retrieve the data from CoreData:
using a one-to-one relationship, thus myArrays points to just one array element
NSArray *positionArray = [myArrays.array valueForKey:#"myArray"];
If you are using a one-to-many, and things are named as above, you'll get back an NSSet.
Core Data should store the Array as a Blob in the database, and a large Array can be written very quickly, say one with 3,500 objects takes less than a second. The performance is comparable to how UIImage is stored and retrieved using pretty much the same concepts. The retrieval I think is even faster.
The alternative is to write each value of the Array individually into Core Data. For this you need to create the appropriate NSManageObject, but beware that you'll have to save 3,500 times for each array value, and for 3,500 items, this will take 20 to 30 seconds.
Thus the above method is great for writing large arrays into CoreData in one shot, and retrieving them also in one shot.
Spent a few hours on this one, was about to give up, and then I saw the light!
NSManagedObject and NSCoding really do not play well together. Consider this answer to a similar question for background and a possible solution.
Related
After a ASIFormDataRequest , i create a temporary NSMutableArray *resultArray from the JSON then add it to a defined NSMutablearray *myData
-(void)viewDidLoad{
myData = [[NSMutableArray alloc] init];
//request that calls gotInfo method
}
-(void)gotInfo:(ASIFormDataRequest *)request{
NSString *responseString = [request responseString];
NSMutableArray *resultArray = [responseString yajl_JSON];
[myData addObject:resultArray];
}
-(IBAction)doSomethingWithData:(id)sender{
//something with myData
}
but when i try to call myData from outside of the gotInfo: method, i get bad access errors and when i inspect myData outside of the method, it shows a kern_protection_failure. So i'm guessing that outside of the method, the resultArray is obviously released, but it's also released from myData since the object inside myData is sharing the same memory location?
I also tried
-(void)gotInfo:(ASIFormDataRequest *)request{
NSString *responseString = [request responseString];
[myData addObject:[responseString yajl_JSON]];
}
How do I preserve myData??
in my header file:
#import <UIKit/UIKit.h>
#class ASIFormDataRequest;
#interface EventsTableController : UITableViewController <UITableViewDataSource>{
NSMutableArray *myData;
}
-(void)gotInfo:(ASIFormDataRequest *)request;
UPDATE:
so in the gbd, the myData is allocated as 0x5e96560 so i did
po 0x5e96560
and then i get the EXC_BAD_ACCESS with the reason being KERN_PROTECTION_FAILURE at address: 0x00000009
but if i do
po [[0x5e96560 objectAtIndex:0] objectForKey:#"key"]
then i get the value! whyyyyyy?
#property(nonatomic,retain) NSMutableArray *myData
and create the object
self.myData = [[NSMutableArray alloc] init];
and
// and i assume your resultArray is a mature NSMutableArray object
[self.myData addObject:resultArray];
The best way of using copy I can think of, is to always set NSString properties to "copy" instead of retain. That way you get more accurate readings from the Leaks instrument if you mess up and forget to release a string an object is holding onto. Other uses of copy need to be more carefully thought out.
NOTE : You are responsible to release myData after no use of that variable.
You dont really have any way to correctly access myData as you declare it as a member inside of EventsTableController, but you dont set the #property for it, and do not synthesize it either. By synthesizing it in your EventsTableController.m file you are telling xcode to generate the getter/setters you need to correctly touch myData, which is where your program seems to be failing. If you do this, this should solve your problem.
-Karoly
Except for the different name of your ivar (mienVar vs. myVar), I don't see a problem. Some other code must be releasing your ivar, or you are accessing it before viewDidLoad has the opportunity to actually create the array (I bet it is the latter).
I think you should put the code in viewDidLoad in your initialization method instead. Don't forget to release the array in dealloc.
You could, of course, also write your own myData getter method, doing lazy initialization, instead of creating it in the init method:
- (NSMutableArray *) myData
{
if (!myData)
myData = [[NSMutableArray alloc] init];
return myData;
}
Note that now, you should access self.myData if you want to use it.
I think the NSString yajl_JSON category can return an array or a dictionary - you might need to inspect the type of the result array on the line below as it may be an NSDictionary:
NSMutableArray *resultArray = [responseString yajl_JSON];
IF you are treating it as an array when its a dictionary that might be causing your problems.
(relevant code from the NSObject+YAJL category below)
YAJLDocument *document = [[YAJLDocument alloc] initWithData:data parserOptions:options error:error];
id root = [document.root retain];
[document release];
return [root autorelease];
(and in YAJLDocument object)
#interface YAJLDocument : NSObject <YAJLParserDelegate> {
(id root_; // NSArray or NSDictionary
I have the following case. I import data from an xml feed and from facebook graph api, in this case posts. I want to merge this data in a array and sort this on the included date data.
I have now the following:
[containerArray addObject: [NSMutableArray arrayWithObjects: created_time, message, picture, fbSource, nil ]
];
This creates a 2-dimensional array, but i want to order all the entries on created_time.
How can i best solve this problem? Thnx in advance!!
Create a data class containing the necessary instance variables instead of the mutable array. Then you can use the various sort method of the NSArray class, for example sortedArrayUsingDescriptors.
A sort could look like this:
NSSortDescriptor *sortDescriptor = [[[NSSortDescriptor alloc] initWithKey:#"created_time"
ascending:YES] autorelease];
NSArray *sortedArray = [containerArray sortedArrayUsingDescriptors:[NSArray arrayWithObject:sortDescriptor]];
[sortDescriptor release];
EDIT
To quote Mr. Fowler from his book Refactoring: Improving the Design of Existing Code.
Replace Array with Object
You have an array in which certain elements mean different things.
Replace the array with an object that has a field for each element
...
Motivation
Arrays are a common structure for organizing data. However, they should be used only to contain a collection of similar objects in somre order.
That's what we want to do here. Let's create a simple Posts class. You can easily add your custom initializer which accepts the four values as parameters, or even a convenience class method to return an autoreleased object later on. This is just a basic skeleton:
Post.h
#interface Posts : NSObject
{
NSDate *created_time;
NSString *message;
UIImage *picture;
id fbSource; // Don't know what type :)
}
#property (nonatomic, retain) NSDate *created_time;
#property (nonatomic, copy) NSString *message;
#property (nonatomic, retain) UIImage *picture;
#property (nonatomic, retain) id fbSource;
#end
Post.m
#import "Post.h"
#implementation Post
#synthesize created_time, message, picture, fbSource;
#pragma mark -
#pragma mark memory management
- (void)dealloc
{
[created_time release];
[message release];
[picture release];
[fbSource release];
[super dealloc];
}
#pragma mark -
#pragma mark initialization
- (id)init
{
self = [super init];
if (self) {
// do your initialization here
}
return self;
}
EDIT 2
Adding a Post object to your array:
Post *newPost = [[Post alloc] init];
newPost.reated_time = [Date date];
newPost.message = #"a message";
newPost.picture = [UIImage imageNamed:#"mypic.jpg"];
// newPost.fbSource = ???
[containerArray addObject:newPost];
[newPost release];
This has been asked before and people have given very good instructions on how to do this, e.g. here.
However, I was wondering if I really need to work with NSCoder if I simply wanted to save one NSMutableArray (containing various instances of another NSMutableArray) to a file? I tried this but only got an error message:
-(void)saveLibraryDat {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0]; // Get documents directory
NSString *filePath = [documentsDirectory stringByAppendingPathComponent:#"myLibrary.dat"];
NSError *error;
[myLibrary writeToFile:filePath atomically:YES];
if (error) {
NSLog(#"There was an error saving myLibrary.dat: %#", error);
}
}
My error message:
2011-05-13 22:00:47.840 MoleNotes[15437:207] There was an error saving myLibrary.dat: (
1,
2
)
So I guess I have to work with NSCoder, right? If so, I was wondering how to go about this. People have explained how to do this with a class, but in my case, I have a NSMutableArray (myLibrary) which contains various instances of a class. Will I have to implement the NSCoder in this class and the NSMutableArray?
I alloc my library like this:
myLibrary = [[NSMutableArray alloc] init];
And then add instances of a class called NoteBook.m like this:
NoteBook *newNoteBook = [[NoteBook alloc] init];
newNoteBook.titleName = #"Notes"; // etc.
[myLibrary addObject:newNoteBook];
So where exactly do I put the NSCoder commands? Only into my NoteBook.m class? Will this automatically take care of myLibrary?
Thanks for any suggestions.
EDIT:
So I've updated my code, but I guess the big problem is that my NSMutableArray myLibrary contains several instances of a custom class I've set up (called notebook). I have set up NSCoding for this class (and all its variables) so that I can save it and load it.
Now my app works totally fine if I create the NSMutableArray in the app (i.e. when the app is started for the very first time, no file exists), instead of loading it from disk:
-(void) setupLibrary {
myLibrary = [[NSMutableArray alloc] init];
NoteBook *newNoteBook = [[NoteBook alloc] init];
newNoteBook.titleName = #"Notes";
/...
If I load it from disk, it works fine as well:
-(void)loadLibraryDat {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0]; // Get documents directory
NSString *filePath = [documentsDirectory stringByAppendingPathComponent:#"myLibrary.dat"];
myLibrary = [[NSMutableArray alloc] init];
myLibrary = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath];
if (!myLibrary) {
// if it couldn't be loaded from disk create a new one
NSLog(#"myLibrary.dat empty... set up new one");
[self setupLibrary];
} else { NSLog(#"Loading myLibrary.dat successful."); }
}
If I log everything which is contained in my library after loading it, everything is still fine. E.g. the following works totally fine:
NSLog(#"%#", [[self.myLibrary objectAtIndex:0] titleName]);
The big problem is, however, if any other method tries to access myLibrary. For instance, if I call the very same log command from another method, the app will crash and I get this error message:
[NSCFString objectAtIndex:]: unrecognized selector sent to instance 0x4b38510
2011-05-14 14:09:10.490 Notes[17091:207] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[NSCFString objectAtIndex:]: unrecognized selector sent to instance 0x4b38510'
This sounds to me as if myLibrary has become deallocated somehow, but I can't see why. How could this have happened? I have the feeling that I did something wrong in my NSCoding set up... because if I simply create myLibrary in code, everything works like wonderfully. It's only if I load it from the disk, that the app will crash.
Here is the class setup:
#import <Foundation/Foundation.h>
#interface NoteBook : NSObject <NSCoding> {
NSString *titleName;
NSString *fileName;
NSMutableArray *tabTitles;
NSMutableArray *tabColours;
NSMutableArray *tabReference;
}
#property (nonatomic, retain) NSString *titleName;
#property (nonatomic, retain) NSString *fileName;
#property (nonatomic, retain) NSMutableArray *tabTitles;
#property (nonatomic, retain) NSMutableArray *tabColours;
#property (nonatomic, retain) NSMutableArray *tabReference;
-(id)initWithCoder:(NSCoder *)aDecoder;
-(void)encodeWithCoder:(NSCoder *)aCoder;
#end
//
// NoteBook.m
#import "NoteBook.h"
#implementation NoteBook
#synthesize titleName, fileName, tabTitles, tabColours, tabReference;
- (id)initWithCoder:(NSCoder *)aDecoder {
self = [super init];
if (self) {
self.titleName = [aDecoder decodeObjectForKey:#"titleName"];
self.fileName = [aDecoder decodeObjectForKey:#"fileName"];
self.tabTitles = [aDecoder decodeObjectForKey:#"tabTitles"];
self.tabColours = [aDecoder decodeObjectForKey:#"tabColours"];
self.tabReference = [aDecoder decodeObjectForKey:#"tabReference"];
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)aCoder {
[aCoder encodeObject:titleName forKey:#"titleName"];
[aCoder encodeObject:fileName forKey:#"fileName"];
[aCoder encodeObject:tabTitles forKey:#"tabTitles"];
[aCoder encodeObject:tabColours forKey:#"tabColours"];
[aCoder encodeObject:tabReference forKey:#"tabReference"];
}
#end
EDIT:
I think I've solved it... I forgot a little 'self'... which messed it all up and deallocated myLibrary:
self.myLibrary = [NSKeyedUnarchiver
unarchiveObjectWithFile:filePath];
if (self.myLibrary == nil) {
NSLog(#"myLibrary.dat empty... set up new one");
[self setupLibrary];
} else { NSLog(#"Loading myLibrary.dat successful."); }
Your code is busted. The "error" variable is uninitialized and never set, so when you check it, you're just seeing random garbage data. If you want to know whether the write was successful, check the return value of writeToFile:atomically:. It will be YES if the write succeeded and NO if it didn't.
However, NSArray's writeTo… methods are for creating plists. If non-property-list objects are in your array, that method isn't appropriate, and an archiver is what you want. Just do something like [[NSKeyedArchiver archivedDataWithRootObject:myLibrary] writeToFile:writeToFile:filePath atomically:YES].
To make your objects conform to NSCoding correctly, just have them implement initWithCoder: and encodeWithCoder:, and in those methods, use NSCoder's storage methods to store the object's instance variables (and the retrieval methods to get them back out).
NSCoder is a protocol that your class must conform to in order to be archived to data/file. Works something like Serealizabe in Java.
Add conformance to the class header like this:
#interface NoteBook : NSObject <NSCoder> { // …
And then you must implement two methods:
-(id)initWithCoder:(NSCoder)decoder;
{
self = [super initWithCoder:decoder];
if (self) {
_someIvar = [decoder decodeObjectForKey:#"someKey"];
// And more init as needed…
}
return self;
}
-(void)encodeWithCoder:(NSCoder)coder;
{
[super encodeWithCoder:coder];
[coder encodeObject:_someIvar forKey#"someKey"];
/// Etc…
}
I would also advice against using -[NSArray writeToFile:atomically:] since in work with property list compliant objects only, not coding compliant classes. The property list object are NSString, NSData, NSArray, or NSDictionary, NSDate, and NSNumber. The list can not be extended.
Instead use NSKeyedArchiver/NSKeyedUnarchiver. Almost as simple to use:
if (![NSKeyedArchive archiveRootObject:yourArrat toFile:path]) {
// It failed.
}
The following is implemented as a class method in a category on one of my managed object entities, with a bunch of other operations:
+ (NSArray*)newArrayOfAllOtherBibs
{
NSManagedObjectContext* moc = [(MyAppDelegate *)[[UIApplication sharedApplication] delegate] managedObjectContext];
NSFetchRequest* fetchRequest = [[NSFetchRequest alloc] init];
NSString* entityName = NSStringFromClass([self class]);
NSEntityDescription* entity = [NSEntityDescription entityForName:entityName inManagedObjectContext:moc];
[fetchRequest setEntity:entity];
NSError* error;
NSArray* items = [moc executeFetchRequest:fetchRequest error:&error];
[fetchRequest release];
NSMutableArray* bibArray = [NSMutableArray array];
for(int i = 0; i < [items count]; i++)
{
Person* person = [items objectAtIndex:i];
if(![person.isSelf isEqualToString:#"YES"])
{
// crash here!
if([person.bib length] > 0)
// crash here!
[bibArray addObject:person.bib];
}
}
return [bibArray retain];
}
So it is supposed to look at all entities for Person, returned for a very simple fetch, and add all that are not marked as "self" (attribute isSelf, type NSString) to a new array which is returned to the caller. All the other methods for add/delete/find matching are working well.
Using
NSString* entityName = NSStringFromClass([self class]);
is an attempt to make the function more generic so I can cut&paste these functions for other entities as required with less editing.
Person looks like this:
#interface Person : NSManagedObject
{
}
#property (nonatomic, retain) NSString * bib;
#property (nonatomic, retain) NSString * isSelf;
[...]
#end
Question is, what could explain EXC_BAD_ACCESS on the line marked? Analyzer shows nothing. At that point person is clearly OK or I would expect it to die earlier, on accessing .isSelf. (It could be made a boolean but I find having two kinds of boolean, one BOOL and one NSNumber, error prone). If person.bib is nil then calling length on it should still return 0 right? But it can't be nil anyway since "bib" is the key for these entities and one is never created without it. At this stage nothing has been deleted from the store, it's a fresh store that has been saved since last addition.
The cause of the problem:
NSDictionary *resultDict = [responseString JSONValue];
NSString* bib = [resultDict objectForKey:#"bib"];
person = [NSEntityDescription insertNewObjectForEntityForName:#"Person" inManagedObjectContext:moc];
person.bib = bib;
[... lots of stuff setting up relationships for person etc.]
NSError *error;
if (![moc save:&error])
{
NSLog(#"Core Data Save error %#, %#", error, [error userInfo]);
}
The fix:
person.bib = [bib copy];
seems like the bib string was not valid at the time the Person entity was saved.
The best thing to do is set NSZombieEnabled in the executable and debug it from there. This way you'll be able to see what is causing the problem.
If you are loading some vars lazily make sure you set them to nil when you release them because if not next time when accessed them. They will not be "skipped", you app will try to use them but since they are not pointing to a valid object it fails and gives you errors like EXC_BAD_ACCESS
Person.bib may not have been loaded yet.
Did you leave the bib property defined as #dynamic and not #synthesize?
Did you override the bib accessor method? If so your override needs to be KVO friendly.
I´ve some trouble with implementing Core Data to my existing iPhone-Project. First I wanna give you a more detailed view on it:
Some of my classes are nested into each other: The class "Game" has an NSArray with objects of class "Player", the class "Player" has an NSArray with objects of class "Item" in turn.
What I wanna do is saving an instance of my "uppest" class "Game" (e.g. when leaving my app).
I tried out some tutorials about Core Data, but there are still some questions:
Do I have to create an entity for each of my classes or just for "Game"?
If I have to do it for each one: I think I will have to create ALL relationships between my classes, but: How to create the relationships e.g. between "Game" an "Player" (please remind: I hold MANY players in ONE NSArray)..
What about changing my existing project? What I allready did is copying the missing methods into my AppDelegate. But what will I have to do with my classes, especially with Getter/Setter-methods? Just change "#synthesize" to "#dynamic" in the implementation?
I hope for some light in my dark ;)
Thanks a lot right now
Mac1988
What I recommend is to setup your database model in xcode, then when you have done that... choose the entities and choose from the menu File > New File. Then choose the "Managed Object Class" from the "Cocoa touch class". After "Next" choose where to save the files, and at the next step XCode will ask you which entities should be generated to files.
After you have done that, you can implement the functions you need into your e.g. you delegate. My recommendation is to leave your existing stuff as it is and use the core data classes as their own. Just pull the data you need from you existing classes/arrays and put them in to the database as you need them. When retrieving, the other way around... get them from the DB and add them to your functions / classes.
Example from one of my projects:
The .h file
#class quicklistSet;
#interface rankedAppDelegate : NSObject <UIApplicationDelegate, UITabBarControllerDelegate> {
[...]
NSMutableArray *_searchHistory;
NSMutableArray *_quickList;
}
[...]
#property (nonatomic, retain) NSMutableArray *_searchHistory;
#property (nonatomic, retain) NSMutableArray *_quickList;
/* Quicklist functions */
- (void)addToQuicklist:(quicklistSet *)theQuicklistSet;
- (BOOL)checkIfQuicklistExists:(quicklistSet*)theQuicklistSet;
- (NSMutableArray *)getQuicklists;
- (void)deleteQuicklist:(NSNumber*)theAppId;
#end
The .m file
#import "quicklistSet.h"
#import "quicklist.h"
#implementation rankedAppDelegate
#synthesize window;
#synthesize tabBarController;
#synthesize _searchHistory, _quickList;
[...]
/* Quicklist functions */
- (void)addToQuicklist:(quicklistSet *)theQuicklistSet
{
BOOL exists = [self checkIfQuicklistExists:theQuicklistSet];
if(!exists)
{
quicklist *theQuicklist = (quicklist *)[NSEntityDescription insertNewObjectForEntityForName:#"quicklist"
inManagedObjectContext:self.managedObjectContext];
[theQuicklist setAppName:[theQuicklistSet _appName]];
[theQuicklist setAppId:[theQuicklistSet _appId]];
[theQuicklist setAppImage:[theQuicklistSet _appImage]];
[theQuicklist setCountryId:[theQuicklistSet _countryId]];
[theQuicklist setCategoryId:[theQuicklistSet _categoryId]];
[theQuicklist setLastCheck:[theQuicklistSet _lastCheck]];
[theQuicklist setLastRank:[theQuicklistSet _lastRank]];
[_quickList addObject:theQuicklist];
[self saveAction];
}
else {
NSLog(#"Existing quicklistSet: %#", [theQuicklistSet _appName]);
}
}
- (BOOL)checkIfQuicklistExists:(quicklistSet*)theQuicklistSet
{
// Get the categories
NSMutableArray *quicklistArray = [self getQuicklists];
BOOL exists = NO;
for(quicklist *dbQuicklist in quicklistArray)
{
if([[dbQuicklist appId] isEqualToNumber:[theQuicklistSet _appId]])
{
exists = YES;
continue;
}
}
return exists;
}
- (NSMutableArray *)getQuicklists
{
if(_quickList == NULL)
{
NSLog(#"Array is null");
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"quicklist"
inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
NSError *error;
NSArray *items = [[self.managedObjectContext
executeFetchRequest:fetchRequest error:&error] retain];
NSMutableArray *returnArray = [[[NSMutableArray alloc] initWithArray:items] retain];
_quickList = returnArray;
[fetchRequest release];
}
else {
NSLog(#"Not null. Count: %d", [_quickList count]);
}
return _quickList;
}
- (void)deleteQuicklist:(NSNumber*)theAppId
{
NSLog(#"Delete row");
// Create a new managed object context for the new book -- set its persistent store coordinator to the same as that from the fetched results controller's context.
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"quicklist"
inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"appId=%#",theAppId];
[fetchRequest setPredicate:predicate];
NSError *error;
NSArray *items = [self.managedObjectContext
executeFetchRequest:fetchRequest error:&error];
[fetchRequest release];
if([items count] > 0)
{
NSManagedObject *eventToDelete = [items objectAtIndex:0];
[self.managedObjectContext deleteObject:eventToDelete];
[self saveAction];
}
}
/* END Quciklist functions */
[...]
#end
EDIT:
The quicklistSet was my existsing class, the quicklist is my coredata class.
Yes, you want to create an entity for all of the classes you mentioned.
You've already got the answer to this in your question: make a one-to-many relationship. For example, for the players relationship of Game, click the "To-many relationship" checkbox in the data model editor.
You'll want to have your data classes (Game, Player, Item) inherit from NSManagedObject. You'll probably want to remove all of the instance variables that correspond to the attributes you added in Core Data. For the to-many relationships (players, items), you'll definitely want to get rid of the NSArray member variable you were using. Instead, do like you were saying and create #dynamic accessors for the players and items properties. Note that you want to use an NSSet instead of an NSArray for players and items.
For example, the header for your Game class might look like this:
#interface Game : NSManagedObject {
}
#property(nonatomic, retain) NSSet *players
#property(nonatomic, retain) NSString *someOtherProperty;
#property(nonatomic, retain) NSNumber *yetAnotherProperty;
#end
And then your implementation file might look like:
#import "Game.h"
#implementation Game
#dynamic players, someOtherProperty, yetAnotherProperty;
- (void)awakeFromInsert {
// do initialization here
}
// other methods go here
#end
Also, be careful when modifying the players and items properties. The Using Managed Objects section of the Core Data Programming guide has a lot of good details, but basically to add a Player to a Game instance, you would do
[game addPlayerObject:newPlayer];
To actually create the new player, you would do something like:
NSManagedObject *newPlayer = [NSEntityDescription insertNewObjectForEntityForName:#"Player" inManagedObjectContext:context];