views:

194

answers:

1

I'm trying to follow Apple's Core Data utility Tutorial. It was all going nicely, until...

The tutorial uses a custom sub-class of NSManagedObject, called 'Run'. Run.h looks like this:

#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>

@interface Run : NSManagedObject {
    NSInteger processID;
}

@property (retain) NSDate *date;
@property (retain) NSDate *primitiveDate;
@property NSInteger processID;

@end 

Now, in Run.m we have an accessor method for the processID variable:

- (void)setProcessID:(int)newProcessID {
    [self willChangeValueForKey:@"processID"];
    processID = newProcessID;
    [self didChangeValueForKey:@"processID"];
}  

In main.m, we use functions to set up a managed object model and context, instantiate an entity called run, and add it to the context. We then get the current NSprocessInfo, in preparation for setting the processID of the run object.

    NSManagedObjectContext *moc = managedObjectContext();

    NSEntityDescription *runEntity = [[mom entitiesByName] objectForKey:@"Run"];
    Run *run = [[Run alloc] initWithEntity:runEntity insertIntoManagedObjectContext:moc];

    NSProcessInfo *processInfo = [NSProcessInfo processInfo];

Next, we try to call the accessor method defined in Run.m to set the value of processID:

[run setProcessID:[processInfo processIdentifier]];

And that's where it's crashing. The object run seems to exist (I can see it in the debugger), so I don't think I'm messaging nil; on the other hand, it doesn't look like the setProcessID: message is actually being received. I'm obviously still learning this stuff (that's what tutorials are for, right?), and I'm probably doing something really stupid. However, any help or suggestions would be gratefully received!

===MORE INFORMATION===

Following up on Jeremy's suggestions:

The processID attribute in the model is set up like this:

NSAttributeDescription *idAttribute = [[NSAttributeDescription alloc]init];
    [idAttribute setName:@"processID"];
    [idAttribute setAttributeType:NSInteger32AttributeType];
    [idAttribute setOptional:NO];
    [idAttribute setDefaultValue:[NSNumber numberWithInteger:-1]];

which seems a little odd; we are defining it as a scalar type, and then giving it an NSNumber object as its default value. In the associated class, Run, processID is defined as an NSInteger. Still, this should be OK - it's all copied directly from the tutorial.

It seems to me that the problem is probably in there somewhere. By the way, the getter method for processID is defined like this:

- (int)processID {
    [self willAccessValueForKey:@"processID"];
    NSInteger pid = processID;
    [self didAccessValueForKey:@"processID"];
    return pid;
}

and this method works fine; it accesses and unpacks the default int value of processID (-1).

Thanks for the help so far!

A: 

If you are getting EXC_BAD_ACCESS on

[run setProcessID:[processInfo processIdentifier]];

it's almost certainly due to one of the pointers no longer pointing to a real object. Either run has been dealloc'd or processInfo has been dealloc'd. This assumes that the line is not the next line of code after

NSProcessInfo *processInfo = [NSProcessInfo processInfo];

If it is, then both objects should be valid, so you are probably looking at something wrong with

[self willChangeValueForKey:@"processID"];

or

[self didChangeValueForKey:@"processID"];

if you have any objects observing that key, it's possible they have gone stale somehow.

JeremyP
Thanks, Jeremy. I suspect that the pointers are valid, because (a) I've now tried running with zombies enabled, and I get the same crash, and (b) Separately, I've tried storing the result of [processInfo processIdentifier] to an int variable (which works), and then using that variable as the argument to setProcessID: - and that crashes just as before. In fact, just doing [run setProcessID:1] crashes as well.So, it looks like the second possibility - a KVO problem. I haven't set any objects to observe processID, but presumably something in Core Data (the context?) observes the keys?
Is processID a defined property in your data model? If it is, it is probably expected to be a NSNumber.
JeremyP
In fact, in order for KVC to work, the property probably needs to be an object. valueForKEy: returns an id.
JeremyP
A common cause of this problem is not setting the proper managed object class in the data model. If you have a custom class but have the default class in the data model you will have this type of trouble.
TechZen
TechZen - that's it! I'd missed out a crucial line: [runEntity setManagedObjectClassName:@"Run"]; - and it now works! Many thanks to everyone that had a look at this for me.