NSError * returned with bad address - why? - objective-c

In the code below, the NSError, when returned, shows in XCode with a class type of 'HomeViewController' - one of my view controllers. This would make sense if I'd done something out of the ordinary with the double pointer, but I didn't. Why is this happening? Is there some stupid mistake in my code, a bug in Core Data, or ????
Hopefully I'm not making a fool of myself !
self->ctx = [(AppDelegate *) [[UIApplication sharedApplication] delegate] managedObjectContext];
NSFetchRequest * request = [[NSFetchRequest alloc] init];
NSEntityDescription *entityDescription = [NSEntityDescription
entityForName:#"Street" inManagedObjectContext:self->ctx];
NSSortDescriptor* sortDescriptor = [[NSSortDescriptor alloc]
initWithKey:#"street" ascending:YES];
NSArray* sortDescriptors = [[NSArray alloc] initWithObjects: sortDescriptor, nil];
[request setEntity:entityDescription];
[request setSortDescriptors:sortDescriptors];
NSError * error;
NSArray *array = [self->ctx executeFetchRequest:request error:&error];
if(error!=nil){
NSLog(#"%#", #"Critical model error search");
}
_streets = array;

Clearly the method you're using ([self->ctx executeFetchRequest:request error:&error]) doesn't set its argument to nil if there isn't an error, just to the error if one occurs. Initialize your variable to nil: NSError *error = nil;.
As pointed out in the comments, you have to check the return value of the call, and only if it's nil or NO (depending on the declared return type) is the NSError pointer guaranteed valid (incl. nil), even if it was valid going in. So instead of if(error != nil), use if(!array) or if(array == nil).

This keeps coming up. So, some concrete examples.
There is no reason to initialize the NSError to nil. The one caveat is if the object you are calling with the &err is nil, you'll get a false failure and checking an un-initialized error will blow up. However, that failure mode is very different than an attempt that leads to failure and, really, you should avoid the nil recipient case (imagine if your managed object context is unexpectedly nil-- you probably have a much bigger issues than a false error).
This is a perfectly valid implementation of a method that returns BOOL and takes an NSError**. Valid, but obviously contrived. However, this kind of thing does happen in a many layered API.
- (BOOL)doThis:(NSError**)chaffin
{
if (chaffin) *chaffin = 0x42;
return YES;
}
Calculating the error can be expensive. Pass null for the error parameter if you only need to know success/failure, but not why.

Related

Core Data not updating, updated model

I have a very weird problem that has stumped me rather!
I have a core data entity that i have just added some new attributes to:
deleted - Boolean
deletedDate - Date
I have the following code, that upon pressing sets both those values on the core data object:
- (IBAction)deleteButtonInTable:(id)sender {
//Get the ID of the currently selected item in the table
NSInteger selected = [self.tweetTableView rowForView:sender];
//Create a predicate and fetch the objects from Core Data
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSPredicate *testForTrue = [NSPredicate predicateWithFormat:#"approved == NO"];
NSSortDescriptor *sortDescriptor1 = [[NSSortDescriptor alloc] initWithKey:#"postDate" ascending:NO];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor1, nil];
[request setPredicate:testForTrue];
[request setSortDescriptors:sortDescriptors];
[request setEntity:[NSEntityDescription entityForName:#"Tweet" inManagedObjectContext:_managedObjectContext]];
//Setup the Request
[request setEntity:[NSEntityDescription entityForName:#"Tweet" inManagedObjectContext:_managedObjectContext]];
//Assign the predicate to the fetch request
NSError *error = nil;
//Create an array from the returned objects
NSArray *fetchedObjects = [_managedObjectContext executeFetchRequest:request error:&error];
Tweet *selectedTweet = [fetchedObjects objectAtIndex:selected];
if (selectedTweet) {
selectedTweet.deleted = [NSNumber numberWithBool:TRUE];
selectedTweet.deletedDate = [NSDate date];
NSLog(#"%#",selectedTweet);
[self refreshTableView];
if (! self.tweetTableView){
NSLog(#"Tableview doesn't exist!!)");
}
[[self tweetTableView] reloadData];
[[self managedObjectContext] commitEditing];
[self saveAction:nil];
}
if ([self.autoWriteTweets isEqualToString:#"YES"]){
[self writeTweetsToXML];
[self saveAction:nil];
}
}
Now, if i watch the object in xcode with some breaks, i can see the attribute change on the object as i pass through the function, but i have an Table displaying a datasource, which is filtered to only show objects that have the deleted bool set to true, and nothing ever shows up there.
Now, to make things even more confusing i have a function that exports an array of the objects:
-(void)writeTweetsToXML{
//Create new fetch request
NSFetchRequest *request = [[NSFetchRequest alloc] init];
//Set new predicate to only fetch tweets that have been favourited
NSPredicate *filterFavourite = [NSPredicate predicateWithFormat:#"approved == YES"];
NSSortDescriptor *sortDescriptor1 = [[NSSortDescriptor alloc] initWithKey:self.exportSort ascending:NO];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor1, nil];
[request setSortDescriptors:sortDescriptors];
//Setup the Request
[request setEntity:[NSEntityDescription entityForName:#"Tweet" inManagedObjectContext:_managedObjectContext]];
[request setResultType:NSDictionaryResultType];
//Assign the predicate to the fetch request
[request setPredicate:filterFavourite];
NSError *error = nil;
//Create an array from the returned objects
NSArray *tweetsToExport = [_managedObjectContext executeFetchRequest:request error:&error];
NSAssert2(tweetsToExport != nil && error == nil, #"Error fetching events: %#\n%#", [error localizedDescription], [error userInfo]);
//NSString *documents = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
//NSString *path = [NSString stringWithFormat:#"%#/tweets.xml", documents];
NSString *writeerror;
if(tweetsToExport) {
NSString * exportLocationFull = [[NSString alloc]initWithFormat:#"%#/tweets.xml",self.exportLocation];
BOOL success = [tweetsToExport writeToFile:exportLocationFull atomically:YES];
NSLog(#"Write Status = %d to %#", success, exportLocationFull);
}
else {
NSLog(#"%#",writeerror);
}
}
Now, when i look at the exported file, two things happen which are odd!
Firstly, an object that i have seen have it's deleted value set to true, exports with the value as 0.
Secondly, the deletedDate attribute does not export at all, every, despite it being in the core data model. I can't see any way this can happen as i am doing no specific filtering on the export.
It's like a getter/setter somewhere is broken, but i have checked the class files and everything is as it should be and set to #dynamic.
Any help would be greatly appreciated as i'm a bit lost as to what the hell is going on.
People had warned me about core data's quirks, but this is just plain odd!
Cheers
Gareth
Note 1
As an aside, i am using the exact same code from the first section to set other attributes on objects that are filtered and that seems to work fine!
You should not name an Core Data attribute "deleted", that conflicts with the
isDeleted method of NSManagedObject.
Compare https://stackoverflow.com/a/16003894/1187415 for a short analysis of that problem.
There are other attribute names that cause conflicts, e.g. "updated" (compare Cannot use a predicate that compares dates in Magical Record). Unfortunately, there are no warnings at compile time or runtime,
and the documentation on what acceptable attribute names are is also quite vague.
Things to check:
Did you save your core data entities with [managedObjectContext save:&error] at the appropriate places (e.g. before displaying the new table view data)? Did you check the error variable?
Did you migrate your model correctly with a new model version?
Are you reading the correct attributes and displaying them correctly (in UI or log statements)?
BTW, in your code you are setting the request entity twice.
Try saving the mananged object context before loading the table view.
The boolean deleted may be 0 before and not be changed or it may be auto-initialized (there is an field in the inspector to set default values) to 0. Date fields on the other hand are nil by default.
P.S. Use [NSNumber numberWithBoolean:YES] in Objective-C.

Method returning object with executeFetchRequest

I searched for the past couple hours reading dozens of posts on the topic of memory management in objective-C and I just don't get it. Sorry. I am doing my best!
What I am looking for at the moment is the answer to how to return an object from a method which calls 'executeFetchRequest'.
Here is some code...
+ (Player *)loadPlayerWithPredicate:(NSString *)name:(NSInteger)index
{
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Player" inManagedObjectContext:managedObjectContext];
[request setEntity:entity];
// Set filter predicate
NSString *strFilter = [NSString stringWithFormat:#"%# = %d", name, index];
[request setPredicate:[NSPredicate predicateWithFormat:strFilter]];
// Create the sort descriptors array
NSSortDescriptor *sorter = [NSSortDescriptor sortDescriptorWithKey:name ascending:YES];
[request setSortDescriptors:[NSArray arrayWithObject:sorter]];
NSError *error = nil;
Player *player = nil;
NSArray *array = [managedObjectContext executeFetchRequest:request error:&error];
if ([array count] == 1)
{
player = [array objectAtIndex:0];
//[player retain]; // ???
}
[request release];
return player;
}
But what I really need is for the "player" object to stick around after the method returns.
How can I make 'player' live after being returned?
Should I call 'retain'? (and then I would call 'release' in the receiver)
Should I 'copy' the object?
I read all of the other posts about alloc, copy, new, etc.
I just need a simple example so that I can then derive understanding from it. If you can also show me an example of the receiver calling this method I would appreciate it.
Thanks!
You could use copy, but it would require that the Player class conforms to the NSCopying protocol, which I doubt it does.
The simplest (and probably best) way to do this here is as such:
if ([array count] == 1)
{
player = [[array objectAtIndex:0] retain];
}
[request release];
return [player autorelease];
You are keeping the player from being released for the meantime by retaining it, and then when you return it you use autorelease. This is not strictly necessary, but I think it is good programming practice in this case. This is because your
+ (Player *)loadPlayerWithPredicate:(NSString *)name:(NSInteger)index;
function name would imply (in Obj-C standard practice) that the object returned is autoreleased, thus leaving the memory management of the object up to the caller.
In the class that you call + (Player *)loadPlayerWithPredicate:(NSString *)name:(NSInteger)index, you will need to determine if you want to retain the returned Player (such as setting it to a retain property), or if you want to leave it as is (autoreleased, and thus will likely be released after the method this call is in has finished. Use this if you only need to perform a few actions with it immediately and don't need to hold on to it after).

Should I retain, autorelease or do nothing when returning NSArray of executeFetchRequest: result?

Please have a look at the code below:
- (NSArray *)requestEntities:(NSString *)entityName {
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription
entityForName:entityName inManagedObjectContext:_context];
[fetchRequest setEntity:entity];
NSError *requestError = nil;
NSArray *result = [_context executeFetchRequest:fetchRequest error:&requestError];
[fetchRequest release], fetchRequest = nil;
return result;
}
and I need to use the result somewhere else, in this method, is result correctly returned(without retain or autorelease)? Also, what should its callers do when getting the result, retain it or use it straight away?
Thanks!
The convention is that you're not responsible for objects returned by methods other than those containing new, alloc, or copy. The array you're given is almost certainly already autoreleased, so it will be released when your code finishes and it goes back to the run loop. If you need to hold onto the array beyond that point (e.g., if you want to keep those results around and refer to them to respond to future UI events) then you should retain the array, preferably by assigning it to a retained property.
I heavily recommend you read the memory management programming guide in full, or at least the first few sections which get right to the meat of what you're asking about.

An easy, reliable way to get max value of a core data object's attribute?

Is there an easy (or just reliable) way to find the max value of a core data attribute? Apple's example just does not work (plus it is ridiculously long and complicated for such a simple task). I have spent almost a day on this and haven't been able to find a satisfactory answer. Please help!
I get the same error as in this question: -[NSCFNumber count]: unrecognized selector. Like him, I haven't been able to find a solution to the problem.
This asker thinks he solved the same problem but, like someone else commented, the answer here is apparently wrong.
This question also has trouble with exactly the same code but at least the asker actually didn't get at an exception. It appears that he couldn't get it to work properly though and ended up using a different solution but didn't say what.
One person here got around it by retrieving results sorted and using the top value. Not ideal! However, if I cannot find a solution soon, I think I will have to do the same, or restructure my model or business rules or create and maintain a MaxValue class in my model to get around this...
I found this in the core data programing guide under fetching specific values. It refers to a minimum, but it answers your question. Not as easy as one would hope, but it isn't that hard to follow.
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Event" inManagedObjectContext:context];
[request setEntity:entity];
// Specify that the request should return dictionaries.
[request setResultType:NSDictionaryResultType];
// Create an expression for the key path.
NSExpression *keyPathExpression = [NSExpression expressionForKeyPath:#"creationDate"];
// Create an expression to represent the minimum value at the key path 'creationDate'
NSExpression *minExpression = [NSExpression expressionForFunction:#"min:" arguments:[NSArray arrayWithObject:keyPathExpression]];
// Create an expression description using the minExpression and returning a date.
NSExpressionDescription *expressionDescription = [[NSExpressionDescription alloc] init];
// The name is the key that will be used in the dictionary for the return value.
[expressionDescription setName:#"minDate"];
[expressionDescription setExpression:minExpression];
[expressionDescription setExpressionResultType:NSDateAttributeType];
// Set the request's properties to fetch just the property represented by the expressions.
[request setPropertiesToFetch:[NSArray arrayWithObject:expressionDescription]];
// Execute the fetch.
NSError *error = nil;
NSArray *objects = [managedObjectContext executeFetchRequest:request error:&error];
if (objects == nil) {
// Handle the error
}
else {
if ([objects count] > 0) {
NSLog(#"Minimum date: %#", [[objects objectAtIndex:0] valueForKey:#"minDate"]);
}
}
[expressionDescription release];
[request release];
have a NSManagedObject Subclass called TestEntity, which has one double property called tesetID.
-(double)getNextIndex
{
NSFetchRequest * request = [[NSFetchRequest alloc] init];
[request setEntity:[NSEntityDescription entityForName:#"TestEntity" inManagedObjectContext:self.managedObjectContext]];
NSError * err = nil;
NSArray * array = [self.managedObjectContext executeFetchRequest:request error:&err];
NSNumber * value = [array valueForKeyPath:#"#max.testID"];
return [value doubleValue]+1;
}
-(void)test
{
for (int i = 0; i<25 ; i++) {
TestEntity * entity = [[TestEntity alloc] initWithEntity:[NSEntityDescription entityForName:#"TestEntity" inManagedObjectContext:self.managedObjectContext] insertIntoManagedObjectContext:self.managedObjectContext];
entity.testID = [NSNumber numberWithDouble:[self getNextIndex]];
NSLog(#"our testID is set as: %#",entity.testID);
}
}

NSTokenFieldCell Subclass to force use of Core Data To-Many Relationship

I have come across an interesting conundrum (of course, I could just being doing something horribly wrong).
I would like an NSTokenField to "represent" a relationship in a Core Data Application. The premise is such: You click on a Note from a TableView (loaded from the Notes Array Controller). The token field is then bound (through "value") to the Notes Array Controller selection.Tags. Tags is a to-many relationship on the entity Notes.
Obviously, an NSTokenField will not accept the NSSet that the Array Controller Provides it. To get around this, I subclassed NSTokenFieldCell and overrode its objectValue and setObjectValue: methods. I thought that I could simply translate the NSSet that was being provided to the NSArray that the NSTokenFieldCell expected. (Note: I originally tried overriding these methods on a NSTokenField subclass; however, they were not being called.)
So, I came up with said code:
- (void)setObjectValue:(NSSet*)object {
tagsList = [object copy];
NSMutableArray *displayList = [[NSMutableArray alloc] init];
for (id newObject in tagsList) {
[displayList addObject:[newObject valueForKey:#"Name"]];
}
[super setObjectValue:displayList];
}
- (id)objectValue {
NSArray *displayList = [super objectValue];
NSEntityDescription *tagEntity = [NSEntityDescription
entityForName:#"Tag"
inManagedObjectContext:[appDelegate
managedObjectContext]];
NSMutableSet *returnValue = [[NSMutableSet alloc] init];
for (NSString *token in displayList) {
NSFetchRequest *request = [[[NSFetchRequest alloc] init] autorelease];
[request setEntity:tagEntity];
NSPredicate *predicate = [NSPredicate predicateWithFormat:
#"Name == %#", token];
[request setPredicate:predicate];
NSError *error;
NSArray *results = [[appDelegate managedObjectContext] executeFetchRequest:request error:&error];
if (results == nil) {
NSManagedObject *object = [NSEntityDescription insertNewObjectForEntityForName:#"Tag" inManagedObjectContext:[appDelegate managedObjectContext]];
[object setValue:token forKey:#"Name"];
[returnValue addObject:object];
} else {
[returnValue addObject:[results objectAtIndex:0]];
}
}
return returnValue;
}
It crashes. :( And, surprisingly it crashes on the line that calls [super objectValue]. It gives me the error:
-[NSConcreteAttributedString countByEnumeratingWithState:objects:count:]: unrecognized selector sent to instance ...
Sigh. The sad thing is that when I go into the Core Data XML file and give the Note a Tag, it displays correctly, and [super setObjectValue:] is passed an array of strings. However, as soon as I enter something else and mouse away, I get the error.
I am not sure what to do about this. Can anyone spot anything horribly wrong with this? Thanks.
UPDATE:
If it makes a difference, I do not have a delegate configured for the TokenField.
In typical SO fashion, I found the answer to my own question. It was silly to begin with. I simply needed another ArrayController bound to the Notes selection.Tags set. Then, I bound the NSTokenField to the ArrangedObjects of that Controller, implemented some delegate methods. Boom. Simple.
Silly me.