Relationships during awakeFromInsert

  • In my Core Data Document based application, I need to initialize some
    attributes in instances one class (MOB) using attributes from another
    class (MOA, which is the root object of the document) to which the
    first is related.  Here is what the classes each look like:

    MOA
    _____
    aTitle - string
    seed - double
    myMOBs - one-to-many relationship to MOB

    MOB
    _____
    bTitle - string
    seeded - double
    myMOA - many-to-one relationship to MOA

    In MOB's awakeFromInsert I have the following:

    - (void)awakeFromInsert
    {
      [super awakeFromInsert];

      MOA* anMOA = self.myMOA;
      self.seeded = anMOA.seed;
    }

    What I'm seeing is that self.myMOA is 'nil' in this awakeFromInsert,
    so apparently the relationship has not yet been established.  I've
    tried intercepting setValue:forKey: and setPrimitiveValue:forKey: but
    they don't appear to be called during creation of an MOB.

    Where can I set MOB's 'seeded' using MOA's 'seed'?
  • > In my Core Data Document based application, I need to initialize some
    > attributes in instances one class (MOB) using attributes from another
    > class (MOA, which is the root object of the document) to which the
    > first is related.  Here is what the classes each look like:
    >
    > MOA
    > _____
    > aTitle - string
    > seed - double
    > myMOBs - one-to-many relationship to MOB
    >
    >
    > MOB
    > _____
    > bTitle - string
    > seeded - double
    > myMOA - many-to-one relationship to MOA
    >
    >
    > In MOB's awakeFromInsert I have the following:
    >
    > - (void)awakeFromInsert
    > {
    > [super awakeFromInsert];
    >
    > MOA* anMOA = self.myMOA;
    > self.seeded = anMOA.seed;
    > }
    >
    > What I'm seeing is that self.myMOA is 'nil' in this awakeFromInsert,
    > so apparently the relationship has not yet been established.

    MOB just got allocated.  The -initWithEntity... method is usually
    still on the stack frame.  It doesn't have a .myMOA yet.  It's not
    clear from your description why one would expect otherwise.

    > I've tried intercepting setValue:forKey: and setPrimitiveValue:forKey: but
    > they don't appear to be called during creation of an MOB.

    self.seeded = foo;

    is

    [self setSeeded:foo];

    not

    [self setValue:foo forKey:@"seeded"];

    > Where can I set MOB's 'seeded' using MOA's 'seed'?

    Presumably you have some code somewhere that does something like:
    NSManagedObject* MOB = [NSEntityDescription
    insertNewObjectForEntityForName:@"MOB" inManagedObjectContext:moc];

    Right after that would work.

    If you have a reference to MOA during -awakeFromInsert (like in a
    global, or through the appDelegate), and doing things the easy way
    doesn't work, you could do something like -performSelector:...
    afterDelay:0 or in response to the MOC's
    NSManagedObjectContextObjectsDidChangeNotification.

    --

    -Ben
  • Thanks to Ben for helping me figure 'it' out.

    On Feb 2, 2008, at 1:11 AM, Ben Trumbull wrote:
    >> [snip]
    >> What I'm seeing is that self.myMOA is 'nil' in this
    >> awakeFromInsert, so apparently the relationship has not yet been
    >> established.
    >
    > MOB just got allocated.  The -initWithEntity... method is usually
    > still on the stack frame.  It doesn't have a .myMOA yet.  It's not
    > clear from your description why one would expect otherwise.

    This gets to the crux of the matter: None of the NSManagedObject
    describe at what point in the life-cycle of such an object the
    relationships are established.  It's as surprising that there are no
    accommodations for intercepting the object at a point where it has
    been completely assembled along with its relationships--even a
    notification would help and perhaps
    NSManagedObjectContextObjectsDidChangeNotification would do the trick.

    Making the job even more difficult is that the example implementation
    in the "NSPersistentDocument Core Data Tutorial" leads to some
    assumptions because it is just such a stripped-down, simplified example.

    The problem is that the relationships have not yet been established
    when awakeFromInsert is called.  My attempts to use delayed
    notifications or intercept setValue... also failed.

    Here is the solution, taking the example in the tutorial as a base and
    using 'employee' in place of my MOB:

    1. In the nib file for the editor window, bind the department
    NSObjectController's 'Content Object' to the File's Owner (MyDocument)
    'department' instance.
    2. Still in the nib, bind the employees' NSArrayController's 'Content
    Set' to the department's NSObjectController's selection/employees.
    3. Back in the model (xdatamodel) select the 'department' relationship
    of the 'employee' box, pop up the contextual menu, and select the Copy
    Method Implementations to Clipboard option.
    4. Go the Employee.m and paste to the bottom of the file.
    5. Move the CoreDataGeneratedPrimitiveAccessors interface portion to
    the top of the file.
    6. Add an #import "Department.h".
    7. Move the setDepartment function up into the implementation section
    and add any code to it for initialization of the employee based on the
    department.
    8. Rip out the department function and the validateDepartment function.

    >> Where can I set MOB's 'seeded' using MOA's 'seed'?
    >
    > Presumably you have some code somewhere that does something like:
    > NSManagedObject* MOB = [NSEntityDescription
    > insertNewObjectForEntityForName:@"MOB" inManagedObjectContext:moc];

    This simple application (which is a stripped down version of the one
    used in the Core Data Tutorial) simply connects the 'Add MOB' button
    to the NSArrayController in the nib and uses the default 'Add'
    selector of the array controller.  So, no, there is no code like that.

    > Right after that would work.
    >
    > If you have a reference to MOA during -awakeFromInsert (like in a
    > global, or through the appDelegate),

    In this case, since I'm following the pattern shown in the
    "NSPersistentDocument Core Data Tutorial" and have a document a global
    wouldn't be appropriate.

    where the document has a single instance of an MOA,

    > and doing things the easy way doesn't work, you could do something
    > like -performSelector:... afterDelay:0 or in response to the MOC's
    > NSManagedObjectContextObjectsDidChangeNotification.

    That would have been my next step if I hadn't stumbled upon the (now
    obvious) customization of relationship management possibilities.

    Thanks again,
    Mike