0

I'm sure someone's seen this before and I'm probably missing something obvious... Here's the brief rundown: I have a Core Data app, it has Car and CarMileage, which is more accurately thought of as a car trip (Car <-->>CarMileage). CarMileage contains a few attributes which include mileage (float, length of the trip), and forWork (BOOL). I made a custom CarMileage class that includes mileageForWork (float, returns mileage if forWork = YES and 0 if forWork = NO). I've also made +keyPathsForValuesAffectingMileageForWork return the set of mileage and forWork. Now I need to calculate a total percentMileageForWork for the specific car for that year.

Currently I have in CarMileage:

- (float) mileageForWork {
    float mileageForWork;
    if ([self.forWork boolValue] == YES) {
        mileageForWork = [self.mileage floatValue];
    } else {
        mileageForWork = 0;
    }
    [[NSNotificationCenter defaultCenter] postNotificationName: @"mileageForWorkChanged" object:self];
    NSLog(@"send notification");
    return mileageForWork;
}

Then Car has:

- (void) awakeFromFetch {
    [super awakeFromFetch];
    [self updatePercentMileageForWork];
    [[NSNotificationCenter defaultCenter] addObserver: self
                                             selector: @selector(updatePercentMileageForWork)
                                                 name: @"mileageForWorkChanged"
                                               object: nil];
    NSLog(@"awakeFromFetch");
}

- (void) awakeFromInsert {
    [super awakeFromInsert];
    [[NSNotificationCenter defaultCenter] addObserver: self
                                             selector: @selector(updatePercentMileageForWork)
                                                 name: @"mileageForWorkChanged"
                                               object: nil];
    NSLog(@"awakeFromInsert");
}

When the autosaved file is loaded, I go to the Vehicles section which contains NSArrayController for Car and CarMileage. The log shows "send notification", the value of percentMileageForWork, and "awakeFromFetch", and then the program pauses and the thread tab in XCode pops up with szone_malloc_should_clear, EXC_BAD_ACCESS code = 2.

I'm completely lost on why this is happening. It looks to my less-than-experienced eye that the problem is where the notification should be observed; after all, the post results in a log. Any ideas why it's crashing?

EDIT (Oct. 14) - I tried to change things over to a key value observing model such that in the -awakeFromInsert and -awakeFromFetch of CarMileage, the object adds the Car as an observer of itself. This works as expected for fetches, but when an object is inserted, my -updatePercentMileageForWork method won't fire; it looks like it's because the Car to CarMileage relationship isn't set yet when the observer is added (so it's really not setting an observer at all). I'm guessing there's a way to move the -addObserver:forKeyPath:options:context call to Car inside the method that adds the new CarMileage object to the relationship set, so I don't get that issue. I don't know if I should be messing with Core Data's own code, which is probably highly optimized, and besides, I don't know how that code is supposed to read normally, so I'm worried about messing up my relationships entirely. I've heard something about "mogenerator" but I can't find documentation and I feel a bit uncomfortable using another program I don't understand well when I'm already confused...

Does anyone know where I should call -addObserver:forKeyPath:options:context from, and if it's from where relationships are set, how I would do that? Or how I would do this the original way I intended, with notifications?

1 Answers1

0

Silly, silly me. My -updatePercentMileageForWork method was calling carMileage.@sum.mileageForWork, which would post a new notification that it had recalculated, setting up an infinite loop. So I changed the CarMileage -mileageForWork method to be:

- (float) mileageForWork {
    float newMileageForWork;
    if ([self.forWork boolValue] == YES) {
        newMileageForWork = [self.mileage floatValue];
    } else {
        newMileageForWork = 0;
    }

    if (newMileageForWork == mileageForWork) {
        return mileageForWork;
    } else {
        mileageForWork = newMileageForWork;
        [[NSNotificationCenter defaultCenter] postNotificationName: @"mileageForWorkChanged" object:self];
        NSLog(@"send notification");
        return mileageForWork;
    }

}

That way it will only send the notification if it's actually a new value, not every time. That left one other problem, which was that when a new CarMileage object (ie. a new trip) is added for that Car, both mileageForWork and newMileageForWork would default to 0, so no notification would be sent. I just overrode -awakeFromInsert on CarMileage with mileageForWork = 1 and everything works, though it means the notification loop runs twice when a new object is inserted. It seems to me, though, that having a specific loop run twice is still more efficient than sending a notification every time any object changes, like it would by using NSManagedObjectContextObjectsDidChangeNotification.