Core Data issue - checking if item exists - iphone

I am working on a code example from NSScreenCast that deals with importing to a Core Data application (link). I have the example working for the most part. I am able to push the refresh button, it parses the json and imports it in to the database. However, every time I press the refresh button it re-adds the same data. I have traced it down to the following code.
+ (Brewery *)breweryWithServerId:(NSInteger)serverId usingManagedObjectContext:(NSManagedObjectContext *)moc {
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:[Brewery entityName]];
[fetchRequest setPredicate:[NSPredicate predicateWithFormat:#"serverId = %d", serverId]];
[fetchRequest setFetchLimit:1];
NSError *error = nil;
NSArray *results = [moc executeFetchRequest:fetchRequest error:&error];
NSLog(#"results: %#", results);
if (error) {
NSLog(#"ERROR: %# %#", [error localizedDescription], [error userInfo]);
exit(1);
}
if ([results count] == 0) {
return nil;
}
NSLog(#"results objectAtIndex:0 = %#", [results objectAtIndex:0]);
return [results objectAtIndex:0];
}
What happens is this method is that it tries to see if the item already exists in the database. If this call returns nil, then the code in MasterViewController adds it again to the database. I have done some debugging and the serverId does get passed. Also, the fetchrequest seems to be valid (haven't been able to debug it to be sure). As you can see I have put a NSLog for the results, but it returns an empty result. So, it then goes to if the results count is 0 which it is, it returns nil. Thus my issue. I don't see any where else where this issue could be a problem. Any thoughts?
Mike Riley

I just modified your method, you can just take a try:
+ (Brewery *)breweryWithServerId:(NSInteger)serverId usingManagedObjectContext:(NSManagedObjectContext *)moc {
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:[Brewery entityName]];
[fetchRequest setPredicate:[NSPredicate predicateWithFormat:#"serverId == %d", serverId]];
[fetchRequest setFetchLimit:1];
NSError *error = nil;
// if there's no object fetched, return nil
if ([managedObjectContext countForFetchRequest:fetchRequest error:&error] == 0) {
NSLog(#"!!!WARN: NO Object Matches.");
return nil;
}
// fetch your object
Brewery *result = [[moc executeFetchRequest:fetchRequest error:&error] lastObject];
if (error != nil) {
NSLog(#"ERROR: %# %#", [error localizedDescription], [error userInfo]);
return nil;
}
NSLog(#"results objectAtIndex:0 = %#", result);
return result;
}
Note: -countForFetchRequest:error: is more efficient as it only 'returns the number of objects a given fetch request would have returned'. You can use this method to check whether there's a object that matches.

You're trying to compare a scalar value (NSInteger) with an object value (NSNumber) in your predicate.
Try:
[NSPredicate predicateWithFormat:#"serverId = %#", [NSNumber numberWithInteger:serverId]]

I think there is a problem in your predicate. It should read:
[fetchRequest setPredicate:[NSPredicate predicateWithFormat:#"serverId == %d", serverId]];
You missed the double equals....
Found out it doesn't matter. You should check to make sure your moc is not nil. Are you sure you're passing it in correctly?

Related

Why is executeFetchRequest:fetchRequest leaking memory?

Instruments shows the following code leaks, if I comment out this code there is no leak.
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
// Edit the entity name as appropriate.
NSEntityDescription *entity = [NSEntityDescription entityForName:USER_CORE_DATA inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
NSPredicate *predicte = [NSPredicate predicateWithFormat:#"username == %#", [[User defaultManager] savedUsername]];
[fetchRequest setPredicate:predicte];
// set any predicates or sort descriptors, etc.
// execute the request
[self.managedObjectContext executeFetchRequest:fetchRequest onSuccess:^(NSArray *results) {
} onFailure:^(NSError *error) {
NSLog(#"Error fetching: %#", error);
}];
[fetchRequest release];
Specifically instruments says this line in the code above:
[self.managedObjectContext executeFetchRequest:fetchRequest onSuccess:^(NSArray *results)
It appears to be a leak with fetchRequest and/or the block. Any help will be appreciated, and thanks in advance.
It appears executeFetchRequest:onSuccess:onFailure: is a function you have defined in NSManagedObjectContext category. Ensure the NSArray object instance that you are passing to the onSuccess block is autoreleased.
Actually it turned out that StackMob had a leak in their code, I downloaded there source and fixed it.
- (NSString *)primaryKeyField
{
NSString *objectIdField = nil;
// Search for schemanameId
objectIdField = [[self SMSchema] stringByAppendingFormat:#"Id"];
if ([[[self entity] propertiesByName] objectForKey:objectIdField] != nil) {
return objectIdField;
}
objectIdField = nil; // This line was missing and causing a leak
// Search for schemaname_id
objectIdField = [[self SMSchema] stringByAppendingFormat:#"_id"];
if ([[[self entity] propertiesByName] objectForKey:objectIdField] != nil) {
return objectIdField;
}
objectIdField = nil; // This line was missing and causing a leak
// Raise an exception and return nil
[NSException raise:SMExceptionIncompatibleObject format:#"No Attribute found for `entity %# which maps to the primary key on StackMob. The Attribute name should match one of the following formats: lowercasedEntityNameId or lowercasedEntityName_id. If the managed object subclass for %# inherits from SMUserManagedObject, meaning it is intended to define user objects, you may return either of the above formats or whatever lowercase string with optional underscores matches the primary key field on StackMob.", [[self entity] name], [[self entity] name]];`

Deleting the objects in different entities (Core Data)

I have a database (coredata) with 2 entities (with no relationships)... Inserting and fetching works well in my case.. But deleting part is troubling me a lot.. The object in one entity got deleted but others are nt..
Here is my code:
-(void)deleteObject:(NSString *)entityDescription //entityDescription get entity name
{
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:entityDescription inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
NSError *errors;
NSArray *items = [self.managedObjectContext executeFetchRequest:fetchRequest error:&errors];
NSManagedObject *managedObject=[finalArray objectAtIndex:currentImageIndex];
for (int i=0;i<[items count];i++)
{
if ([managedObject isEqual:[items objectAtIndex:i]])
{
[self.managedObjectContext deleteObject:managedObject];
}
}
NSLog(#"%# object deleted", entityDescription);
NSNotificationCenter *nc1=[NSNotificationCenter defaultCenter];
[nc1 addObserver:self selector:#selector(deleteCheck:) name:NSManagedObjectContextObjectsDidChangeNotification object:self.managedObjectContext];
NSError *error;
if (![self.managedObjectContext save:&error])
{
NSLog(#"error occured during save = %#", error);
}
else
{
NSLog(#"deletion was succesful");
}
This my code I follow the same method for deleting the objects from other entities...The entitydescription gets the different entity name from another method... Itz working well for one entity and not for another...But I'm getting the managedObjectContext deletion successful message(bt not deleted frm DB).. How can I solve this?
A couple of things you might do to narrow it down:
NSArray *items = [self.managedObjectContext executeFetchRequest:fetchRequest error:&errors];
// Add this to see that you are returning items from your fetch
NSLog(#"%i objects returned in items array",items.count);
NSManagedObject *managedObject=[finalArray objectAtIndex:currentImageIndex];
// Add this to see what your fetch is returning
NSLog(#"Fetch returned %#", managedObject);
for (int i=0;i<[items count];i++)
{
// Add this to see for yourself if it is equal to managedObject
NSLog(#"Testing: %#",[items objectAtIndex:i]);
if ([managedObject isEqual:[items objectAtIndex:i]])
{
[self.managedObjectContext deleteObject:managedObject];
// Move this to here so you know each time an object is deleted
NSLog(#"%# object deleted", entityDescription);
}
}
I suspect that you want to test a property of the object, not that the objects are equal. You were reporting "object deleted" when loop finished regardless of whether or not you had deleted anything. If you meant to test a property of the managed object change your test to:
If ([managedObject.propertyToTest isEqual:[items objectAtIndex:i]])
Or a property of each item:
If ([managedObject isEqual:[items objectAtIndex:i].propertyToTest])

Saved string in CoreData returns <null> and the string. Why?

Hi i am saving values coming from a database in a MultableArray and then in CoreData:
NSMultableArray *mutary = [[NSMultableArray alloc] init];
NSManagedObjectContext *context = [app managedObjectContext];
for(int n=0; n<[AttributeArray count]; n++)
{
[mutary addObject:[[AttributeArray objectAtIndex:n] objectForKey:#"AttributName"]];
NSLog(#"%#", mutary);
}
attributeString = [mutary componentsJoinedByString:#","];
raume = [NSEntityDescription insertNewObjectForEntityForName:#"Raum" inManagedObjectContext:context];
raume.raumattribut = attributeString;
if (![context save:&error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
NSLog output for the MultableArray is:
2012-06-20 17:21:00.047 book-app[31984:15803] (
A7OVERHEAD,
Beamer
)
So far its working correct. The two expected values from the database are now in the Array.
Now i am fetching these attributes from CoreData:
NSManagedObjectContext *context = [app managedObjectContext];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Raum" inManagedObjectContext:context];
[request setEntity:entity];
NSError *error = nil;
NSArray *events = [context executeFetchRequest:request error:&error];
for (Raum *att in events)
{
stringAttribute = [[events valueForKey:#"raumattribut"] componentsJoinedByString:#","];
NSLog(#"ATTRIBUTE: %#", stringAttribute);
}
}
So far so good. But if i look now on my NSLog output:
2012-06-20 17:21:00.055 book-app[31984:15803] ATTRIBUTE: <null>,A7OVERHEAD,Beamer
CoreData is returning and then the two values. Where is that coming from?
Can someone help?
Thanks in advance
EDIT: After some investigating and clarifying (see comments), here is my answer:
Calling valueForKey on an NSArray returns an array where each element is created by calling valueForKey on each member of the original array. This means your output indicates that you have 3 total objects returned by your fetch request, and the first one does not have it's raumattribut attribute set.
PREVIOUS ANSWER:
You are calling valueForKey on an instance of NSArray (the query results). Perhaps you need to get objectAtIndex:0 first use the variable you are iterating the values on? valueForKey is likely returning nil.
stringAttribute = [[att valueForKey:#"raumattribut"] componentsJoinedByString:#","];
or similar.
EDIT:
At first I missed the for loop in the second part, I have edited the above code sample to use the attr, which

saving managedObjectContext returns null error

Could anyone see any possible reasons why?
Friend *newFriend = [NSEntityDescription insertNewObjectForEntityForName:#"Friend" inManagedObjectContext:managedObjectContext];
newFriend.name = #"Jim";
newFriend.age = [NSNumber numberWithInt:5];
NSError *error = nil;
if ([managedObjectContext save:&error])
{
NSLog(#"error %#", error);
}
managedObjectContext was passed to the view controller where this code is from the application delegate.
Thanks
if (![managedObjectContext save:&error])
{
NSLog(#"error %#", error);
}
that should be
You should expect error to continue to be nil if save: succeeded (and therefore returned YES as you're testing). What behavior did you expect here?

"[CFString release]: message sent to deallocated instance" when using CoreData

I have started using CoreData in my project. Without the pieces of code from CodeData my project was working pretty fine. I added the methods for accessing the NSManagedObjectContext from the coreData project template. Now I try to create new CoreData object with the following code:
- (void)saveSearchResultToHistory:(NSArray *) productsArray {
[productsArray retain];
NSLog(#"context: %#", self.managedObjectContext);
Product *product = [NSEntityDescription
insertNewObjectForEntityForName:#"Product"
inManagedObjectContext:self.managedObjectContext];
product.productId = [(Product *) [productsArray objectAtIndex:0] productId];
NSError *error;
if (![self.managedObjectContext save:&error]) {
NSLog(#"Whoops, couldn't save: %#", [error localizedDescription]);
}
[productsArray release];
}
When this method is ran once then everything is fine, when I try to run it for the second time, the processing is stopped at:
Product *product = [NSEntityDescription
insertNewObjectForEntityForName:#"Product"
inManagedObjectContext:self.managedObjectContext];
with the following error message in the console:
[CFString retain]: message sent to deallocated instance 0x5a23b0
Any ideas what might be wrong?
Thanks!
First off you do not need to save the context every time you add something, just save when the app closes or goes in the background.
The error you are getting looks like you over release a NSString some where.
To check if the error isn't in the coredata context use this save function:
- (void)saveContext {
if ([self.managedObjectContext hasChanges]) {
NSError *error = nil;
if (![self.managedObjectContext save:&error]) {
dbgPrint(#"Failed to save to data store: %#", [error localizedDescription]);
NSArray* detailedErrors = [[error userInfo] objectForKey:NSDetailedErrorsKey];
if (detailedErrors != nil && [detailedErrors count] > 0) {
for(NSError* detailedError in detailedErrors) {
dbgPrint(#"--DetailedError: %#", [detailedError userInfo]);
}
} else {
dbgPrint(#" %#", [error userInfo]);
}
}
}
}