简体   繁体   中英

Objective C property vs ivar issues with Core Data

I spent several hours trying to figure out this problem, and I think it comes down to my fundamental understanding of Objective C, though it manifested through working on Core Data. I'm not surprised - I've only been working with Objective C and Cocoa Touch for just two months now.

My situation is that I have a series of models that are all connected fine in the CD. My app works just fine until I tried extending it yesterday. I have my main model Job in a view controller as a class property in the.h file. In my viewWillAppear method I have to look up a relationship through another object, so I do something like:

/** project as an ivar **/
NSManagedObject *project = [job valueForKey:@"project"];
NSArray *divisions = [[project valueForKey:@"divisions"] allObjects];
//do something with divisions --> crash

...

/** project as a property **/
project = [job valueForKey:@"project"];
NSArray *divisions = [[project valueForKey:@"divisions"] allObjects];
//do someting, anything ---> A-OK!

So, why does my app crash when I try to do things with the results of [project valueForKey:] unless I make project a class property?

EDIT

It appears that simply including the if(!divisions) conditional in there (which it should be null when the view first loads), it doesn't like the statements I provided above and produces the EXC_BAD_ACCESS . However, when leaving it out, my code works fine.

- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];

    if(!divisions){

        NSManagedObject *project = [[job valueForKey:@"project"] retain];
        NSArray *divs = [[project valueForKey:@"divisions"] allObjects];

        NSSortDescriptor *alphaSort = [NSSortDescriptor sortDescriptorWithKey:@"order" ascending:YES];
        divisions = [[divs sortedArrayUsingDescriptors:[NSArray arrayWithObject:alphaSort]] mutableCopy];

    }
    [[self tableView] reloadData];
}

I'll accept that there's a bigger memory management problem going on. Should I probably go back and re-read some book chapters on Obj-C and retrace my variables so it makes sense?

I can't tell for certain based on the limited code provided but the simplest explaination for the crash is that you don't check if the divs or the divisions arrays are empty before performing any operations on them.

If the Project.divisions relationship is empty this:

NSArray *divs = [[project valueForKey:@"divisions"] allObjects];

... will return an empty array. Any attempt to address any element of the array eg [divs objectAtIndex:0] will produce an error.

Also, divisions appears to be an iVar of the controller object but you don't use either the preferred self.divisions reference form nor a [divisions retain] when assigning the 'divs' array to it. Since the divs array will be returned autoreleased, it will be purged when the pool next drains and the array in divisions will disappear even if it is not empty.

Simply change divisions to self.divisions to fix.

Apart from the ivar confusion your code has two other problems. When accessing an attribute on an NSManagedObject you should use primitiveValueForKey: as in

[job willAccessValueForKey:@"project"];
NSManagedObject *project = [job primitiveValueForKey:@"project"];
[job didAccessValueForKey:@"project"];

Any time you get or set a property of an existing managed object, you should always precede the call with willAccessValueForKey: and close with didAccessValueForKey: so that the model and the store stay in sync.

Even better would be

[job willAccessValueForKey:@"project"];
NSManagedObject *project = [job primitiveProject];
[job didAccessValueForKey:@"project"];

but unfortunately the Xcode debugger doesn't grok that and you have to put up with those annoying yellow warnings.

Furthermore, an NSManagedObject relationship returns an NSSet not an array:

[project willAccessValueForKey:@"divisions"];
NSSet *divisions = [project primitiveValueForKey:@"divisions"];
[project didAccessValueForKey:@"divisions"];

Read more in Apple's Model Object Implementation Guide

Lastly, if you're doing all this to populate a table, you're much better off instantiating an NSFetchedResultsController which is optimized for precisely that task.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM