Core Data Managed Object Context Saving Problem - iphone

I am trying to save an array into core data using NSData but my ManagedObjectContext says there are 0 objects and when I call it, I have it appearing as NULL. I have an entity called Event and 3 attributes in it (chatArray,...,...). I have tried for 11 hours and can't figure it out. I believe I am setting it wrong because the NSData is correct. How should I be setting this???
UPDATE
I am developing a chat application and I have the chat messages in a table view (It's an array of data). I need to save all the chat history when you exit the app and reload it. I have the messages coming in as strings and add it to the array for the table. If I didn't do an array, and I added the messages as strings of text to core data how would I add them to the array for the table view when you reload the app?
#property (nonatomic, retain) NSManagedObjectContext *managedObjectContext;
#property (nonatomic, retain) NSManagedObject *managedObject;
//
NSArray *array = [[[NSArray alloc]initWithObjects:#"one",#"two",#"three", nil]autorelease];
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:array];
NSLog(#"data %#",data);
NSLog(#"Array %#",[NSKeyedUnarchiver unarchiveObjectWithData:data]);
[(Event*)managedObject setValue:data forKey:#"chatArray"];
if ([self managedObject])
{
NSFetchRequest *fetchRequest = [[[NSFetchRequest alloc] init] autorelease];
[fetchRequest setEntity:[NSEntityDescription entityForName:#"Event" inManagedObjectContext:managedObjectContext]];
[(Event *)managedObject setChatArray:data]; }
else {
Event *event = [[[Event alloc] initInsertingIntoManagedObjectContext:managedObjectContext]autorelease];
NSFetchRequest *fetchRequest = [[[NSFetchRequest alloc] init] autorelease];
[fetchRequest setEntity:[NSEntityDescription entityForName:#"Event" inManagedObjectContext:managedObjectContext]];
[event setChatArray:data];
}
NSError *error;
[managedObjectContext save:&error];
NSArray *myArray = [NSKeyedUnarchiver unarchiveObjectWithData:[(Event*)managedObject valueForKey:#"chatArray"]];
NSLog(#"chatArray %#",myArray);

Brandon,
First, in Core Data, BLOBs should be stored in leaf nodes (i.e. an entity that just contains the BLOB and a back to one relationship. (This pattern/convention has emerged because it is almost trivial to get a retain cycle of large blobs when there are other relations in the entity.)
Second, why are you storing these strings as an array and not as an entity with a time stamp, etc.?A BLOB is not particularly more efficient than individual rows plus the system can both search the messages and more flexibly store the rows. SQLite handles strings specially.
Third, it appears that you are composing your class rather than inheriting from your model entity, why? This makes your code more complex.
Finally, It is really hard to tell what you are trying to do. Could you include your full .h file? And the full method declaration?
Andrew

Related

Core Data. ExecuteFetchRequest with fault NSManagedObjects (not in RAM)

I need to execute fetch request. But when I do it I get not fault NSManagedObjects (each of the objects is about 5 Mb, that's why I get the memory warning). Apple provides faulting possibility for Core Data (when objects are not loaded in RAM). And I wanna my objects to use this possibility.
Here is my code
+ (NSMutableSet *)GetImagesWithPredicate:(NSPredicate *)predicate
{
NSString *entityName = kEntityName;
AppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];;
NSManagedObjectContext *context = appDelegate.managedObjectContext;
NSEntityDescription *entityDesctiption = [NSEntityDescription
entityForName: entityName
inManagedObjectContext:context];
// find object
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:entityDesctiption];
[request setPredicate:predicate];
NSArray *objects = [context executeFetchRequest:request error:nil];
[request release];
if (objects == nil)
{
NSLog(#"there was an error");
return nil;
}
NSMutableSet *set = [NSMutableSet setWithArray:objects];
return set;
}
where predicate is (id < 500).
App crashes after
NSArray *objects = [context executeFetchRequest:request error:nil];
because all the data of objects appears in the RAM of iPhone.
It seems that default option returnsObjectsAsFaults = YES doesn't work.
The objects are probably being returned as faults; you can verify this with isFault. The issue is that Core Data automatically pre-fetches the property values for those objects and places them in the row cache (in memory). You can disable this behaviour by setting includesPropertyValues to NO on the NSFetchRequest object.
See the includesPropertyValues documentation for details of all this and the performance implications.
As an aside, you might not want to store lots of large objects in the database directly. You probably should look into using external storage if you're targeting iOS 5, or else using separate files yourself with their names/paths/ids in Core Data.
you could set the - (void)setResultType:(NSFetchRequestResultType)type for the NSFetchRequest and only get the relevant attributes for your Object with the -(void)setPropertiesToFetch:(NSArray *)values Method.
And only lazy loading the needed attributes.

Count Distinct items in Core Data

I have a Core Data entity with a property named 'value' which is often repeated. I wish to retrieve only unique values (done) and also how often each one appears, so that I can sort by that property (I'm building an autocomplete function based on existing user input, so knowing how frequently a certain input has appeared is essential).
My fetch request currently looks like this:
NSFetchRequest *fetchRequest = [[[NSFetchRequest alloc] init] autorelease];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Item" inManagedObjectContext:self.managedObjectContext];
NSDictionary *entityProperties = [entity propertiesByName];
[fetchRequest setEntity:entity];
[fetchRequest setFetchBatchSize:10];
[fetchRequest setFetchLimit:20];
[fetchRequest setReturnsDistinctResults:YES];
[fetchRequest setResultType:NSDictionaryResultType];
[fetchRequest setPropertiesToFetch:[NSArray arrayWithObject:[entityProperties objectForKey:#"value"]]];
NSSortDescriptor *sortDescriptor = [[[NSSortDescriptor alloc] initWithKey:#"value" ascending:YES] autorelease];
NSArray *sortDescriptors = [NSArray arrayWithObject:sortDescriptor];
[fetchRequest setSortDescriptors:sortDescriptors];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"value BEGINSWITH[c] %#",predicateString];
[fetchRequest setPredicate:predicate];
return fetchRequest;
I'm a bit stuck with this one. Any ideas?
Thanks in advance!
I don't like this one, but...
You could do an other fetchRequest with a predicate that exactly match the value you are seeking. Allowing duplicate, then have the count of the array.
This one is better, but more work up front.
An other way could be to have a derived property in your data model that keep track of your duplicated count as you create them.
(with that option you could easily sort by duplicated count)
Ok for a Derived property.
First you will need to subclass NSManagedObject and use that subclass in your data model. (in Xcode 3 there was a way to create that quickly, but I don't know that in Xcode 4) But if you name it the same as your entity I think core data will pick it up.
#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>
#interface Person : NSManagedObject
#property (nonatomic, retain) NSString * name;
#property (nonatomic, retain) NSString * firstLetterOfName;
#property (nonatomic, retain) NSString * phoneNumber;
#end
And in your implementation you will need to do something like this (there is code snippet in Xcode 4 to get you started, but there is a typo in one of them, unless it have been corrected)
#import "Person.h"
#implementation Person
#dynamic name, phoneNumber, firstLetterOfName;
- (void)setName:(NSString *)value {
[self willChangeValueForKey:#"name"];
[self setPrimitiveValue:value forKey:#"name"];
self.firstLetterOfName = [value substringToIndex:1];
[self didChangeValueForKey:#"name"];
}
#end
You can see that the firstLetterOfName is set each time the Name is set.
You can do the same kind of thing with relationship.
So when you add an item to a relationship you should be able to look up your relationship.
Here is an exemple of something similar, where I need to find if the object I'm adding have the lowest price of it's group because of a derived property call isMeilleurPrixAvecPrixElment. (this is old code, so I don't recall every detail of it, it have been done in OSX.4)
- (void)addPrixHistoriqueObject:(PrixElement_MO *)value
{
NSSet *changedObjects = [[NSSet alloc] initWithObjects:&value count:1];
[self willChangeValueForKey:#"prixHistorique" withSetMutation:NSKeyValueUnionSetMutation usingObjects:changedObjects];
if ([self isPrixRegulierAvecPrixElement:value])
[self enleveL_AutrePrixRegulierPourCommerceDeCePrixElement:value];
if ([self isMeilleurPrixAvecPrixElment:value])
[self echangeMeilleurPrixAvecCePrixElement:value];
[[self primitivePrixHistorique] addObject:value];
[self didChangeValueForKey:#"prixHistorique" withSetMutation:NSKeyValueUnionSetMutation usingObjects:changedObjects];
[changedObjects release];
}
In respond to a comment
depending on your data model and the importance of that aspect in your application, I can think of 3 solutions.
1- Redesing your data model around that aspect.
2- When setting the values query the rest of your entity with a predicate and update a property that have the count.
3- (I'm not sure of that one, but is worth trying) NSManagedObect is an object, so maybe you could have a static dictionary that have the value as a key and the count as value.
I would maybe try number 3 first (it look like the easy one), but I've never done something like that. So I'm not sure for the presitance of a class variable in core data.

Core Data object gets invalidated

I have a problem with some Core Data objects that are somehow invalidated.
The managed object context is in the app delegate and I use it in a view table to fetch 'notes' objects from a database and display them.
I build an array for the sections (today, yesterday, etc.) and for each section an array with the notes in the section like this:
// in the .h file
NSMutableArray* data; // An array containing an array of thoughts for each section.
#property (nonatomic, retain, readonly) NSManagedObjectContext* objectContext;
// in the .m file, when loading the view
ThoughtsAppDelegate* appDelegate = (ThoughtsAppDelegate*)[[UIApplication sharedApplication] delegate];
objectContext = appDelegate.managedObjectContext;
NSEntityDescription* descriptor = [[NSEntityDescription entityForName:#"Note"
inManagedObjectContext:objectContext] autorelease];
NSFetchRequest* request = [[NSFetchRequest alloc] init];
[request setEntity:descriptor];
NSError* error;
NSArray* notes = [objectContext executeFetchRequest:request error:&error];
// example for one section
data = [[NSMutableArray alloc] init];
NSMutableArray* ccurrentSection = [[NSMutableArray alloc] init];
[data addObject:currentSection];
for(Note* t in notes)
[currentSection addObject:t];
When the view loads the first 5 notes are displayed (the rest don't fit in the view) and all is OK. But when I scroll down to view the next notes I get an
NSObjectInaccessibleException The NSManagedObject with ID... has been invalidated.
This happens for all objects in the array.
How is this possible? I checked and don't reset/release the context. Or is it bad to store a Core Data object and refer to it later?
Edit: this seems to happen also if I don't scroll and want to display details about a note when it's selected. Seems that as soon the first notes are displayed they're invalidated.
It would appear to be something with the way you manage the notes objects, but the code that is doing this is not in your example. The notes array is an autorelease array so unless you are retaining it somewhere it may be releasing before your load the next section from it.

Difference in data when using core data relationships

I'm using core data to fetch values from an sql lite database for my iphone app. I'm pretty new to the syntax so I might be missing a few key infos.
My fetch request looks like this:
NSEntityDescription *difficultyDescription = [NSEntityDescription entityForName:#"Difficulty" inManagedObjectContext:managedObjectContext];
NSFetchRequest *fetchRequest = [[[NSFetchRequest alloc] init] autorelease];
[fetchRequest setEntity:difficultyDescription];
NSArray *diffResults = [managedObjectContext executeFetchRequest:fetchRequest error:&error];
After that, I'm looping through the results by using this snippet:
systems = [[NSMutableArray alloc] init];
values = [[NSMutableArray alloc] init];
for (NSManagedObject *diff in diffResults) {
[systems addObject:diff];
[values addObject:[diff valueForKey:#"difficultyValues"]];
}
What I don't get about this is, that when I log systems by using
NSLog(#"%#", [[systems objectAtIndex:0] valueForKey:#"name"]);
I get the result in plain text. For example "some value".
When logging the results in the "values" array however I get this result:
{(
"some value"
)}
I'm using the same logging call:
NSLog(#"%#", [[values objectAtIndex:0] valueForKey:#"value"]);
The entities difficulty and difficultyValues have a one to many relationship.
How can I display the value in the array "values" like the one in "systems"? I need it later on for a label.
This seems to be a fundamental misunderstanding. difficultyValues is a relationship, while name is an NSString attribute. That's why it appears differently in NSLog(), which works by sending objects a -description message.
The value for difficultyValues will be an NSSet which contains multiple managed objects for the entity difficulty.
Here's what you should do:
NSLog(#"%#", [[systems objectAtIndex:0] valueForKey:#"name"]); // NSString*
// let's loop through all the difficulties of this item
for (NSManagedObject* aDifficulty in [[[systems objectAtIndex:0] valueForKey:#"difficultyValues"] allObjects]) {
NSLog(#"%#", [aDifficulty valueForKey:#"name"]);
}
This, of course, assuming your difficulty entity has a name attribute.

Coredata without using navigation based application

I need to use coredata in my View based Application. I created core data file. But I can't access to core data through my Viewbased application without using UITableview.
I used just UITextField and buttons only. I want to need insert records, deletion, updation and search all operations in my application.
Does anyone have some sample code without using navigation based with UITableview?
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSManagedObjectContext *context = // Your context pointer here
[fetchRequest setEntity:[NSEntityDescription entityForName:#"ObjectName" inManagedObjectContext:context]];
NSError *error;
NSArray *objects = [context executeFetchRequest:fetchRequest error:&error];
[fetchRequest release];
for (int index=0; index<[objects count]; index++) {
ObjectName *dmg = (ObjectName *)[menus objectAtIndex:index];
}
This will get you your objects out of the Core Data. You can manipulate this as you see fit as long as you commit it it will be saved in your database. It sounds like you should also search Stack Overflow (and maybe also a search engine) for information about Predicates, which allow you to filter your object graph.