Question

My app's main function is to arrange objects on a plot. The whole thing is based on Core Data, and each object on the plot is represented by an entity. For this example, that entity will be "Unit," a custom subclass of NSManagedObject.

When the plot is opened, each Unit gets a UnitViewController created, which KVO-observes changes to the Unit's properties, such as .positionX, .positionY, and .angle. Touch gestures that the UnitViewController detect modify the Unit object, and the view responds by moving itself when it observes them. This allows an easy implementation of Undo and Redo without much extra code, where the Units move themselves around when the database changes.

The problem arises when the Unit is deleted.

One property that the Unit has is .viewController, which points to the UnitViewController that's linked to it. It's a transient property, because obviously I don't need it to persist; I re-create it every time I open the plot. (The UnitViewController has a property .object, which points to its Unit; essentially an inverse property.) The problem is, when I Undo a delete, or Redo the Undo of a creation, Core Data tries to restore the .viewController property, but that UnitViewController object has been deallocated. I was getting an EXC_BAD_ACCESS until I enabled zombies, which gave me this:

*** -[UnitViewController retain]: message sent to deallocated instance 0x18365780

So my question is: how can I get the NSUndoManager to completely ignore that property? I've tried turning off undo registration before modifying it, even to the point of overriding the setter method in Unit+LD.m (my category on Unit.m, which is the NSManagedObject subclass):

- (void)setViewController:(UnitViewController *)viewController
{
    [self.managedObjectContext.undoManager disableUndoRegistration];

    [self willChangeValueForKey:@"viewController"];
    [self setPrimitiveValue:viewController forKey:@"viewController"];
    [self didChangeValueForKey:@"viewController"];

    [self.managedObjectContext.undoManager enableUndoRegistration];
}

But still, it tries to restore the .viewController when I undo a delete. Anyone dealt with anything like this before? Is there a better way to go about this?

Was it helpful?

Solution

I ended up submitting a support ticket to Apple, and they came back immediately with a pretty simple answer: override the setter method -setViewController: in Unit.m and make it empty. (Or in Unit+LD.m, in my case, since I do all my custom stuff in a category to avoid having to re-do it when I update my data model.)

- (void)setViewController:(UnitViewController *)viewController {

}

I only set the property a couple times in the code, and when I do, I just use -setPrimitiveValue:forKey: instead. I still have some testing to do to make sure everything else works, but for now, it seems to work like a charm. The undo manager doesn't ever try to restore the property and I've stopped getting crashes.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top