1

I've been wondering how to do backwards relationships with readonly properties. Lets say there's a parent class:

@interface Parent
@property (strong, readonly) Child * child;
@end

And a child

@interface Child
@property (weak, readonly) Parent * parent;
@end

The problem is that both need the other to have already been initialised, and after that the property is readonly. This first came up when looking into Apple's documentation and seeing it being applied in CBCharacteristic. How do they do that?

Edit: to make it clear I'm not talking about inheritance! Maybe that was a poor choice of a name. This is what I want:

Parent * parent = [Parent new];
Child * child = [Child new];

parent.child = child;
child.parent = parent;

This is a cyclic relationship!

I just came up with this:

Parent * parent = [Parent alloc];
Child * child = [Child alloc];

parent = [parent initWithChild:child];
child = [child initWithParent:parent];

It should OK, no?

Edit: never mind that last edit. Please see the comments below.

André Fratelli
  • 5,920
  • 7
  • 46
  • 87
  • 1
    Your snippet is not a good idea. `init...` is allowed to throw away the instance it started with and return a new one, which would leave the other object holding a garbage pointer. Separating `alloc` and `init` is bad Cocoa style. – jscs Jul 04 '15 at 23:16
  • You are right! And I just noticed that I don't have the child instance when I initialise the parent anyway... – André Fratelli Jul 04 '15 at 23:24

2 Answers2

3

You can give Parent privileged access to Child's parent property by redeclaring the property as readwrite in a private extension which is imported to Parent's implementation file:

//Child+ParentAccess.h
#import "Child.h"

@class Parent;    

@interface Child ()

@property (weak, readwrite) Parent * parent;

@end

Be sure to import Child+ParentAccess.h into Child.m as well.

//Parent.m
#import "Parent.h"
#import "Child.h"
#import "Child+ParentAccess.h"

@implementation Parent

- (void)aMethod
{
    //...
    [aChild setParent:self];    // compiles
    //...
}

But a better option would probably be to let a Child set its own property when it's initialized. The creating Parent should pass itself in to the Child while setting its ivar:

// In Parent.m
_child = [[Child alloc] initWithParent:self];

and the Child instance can set its own ivar directly:

- (id)initWithParent:(Parent *)parent
{
    // init boilerplate
    _parent = parent;
    // etc.
}
Community
  • 1
  • 1
jscs
  • 63,694
  • 13
  • 151
  • 195
  • Wait, it doesn't work after all... I get `unrecognised selector sent to instance` when I call `setParent:` – André Fratelli Jul 05 '15 at 00:28
  • Oops. You're talking about the first option? I said "extension" and then made a category. It has to be an extension. Let me edit... – jscs Jul 05 '15 at 00:33
  • Edit complete. That should do it, but the second option is still the better one. – jscs Jul 05 '15 at 00:36
  • The second option sets the parent in child, but not child in parent. I need both! It works with an extension – André Fratelli Jul 05 '15 at 00:37
  • 1
    The `Parent` instance just sets its `_child` pointer to the new `Child` when it creates it. You should do that with _both_ options. That's what my last sentence said. I've edited the second-to-last code block to make this clearer. – jscs Jul 05 '15 at 00:39
0

I don't think that they both need the other to be already instantiated. Also, I don't think that the fact that the properties in question are both readonly is pertinent to your problem.

I presume that your Child class has a subordinate relationship to your Parent class. Therefore, you can instantiate your Child instance from your Parent instance. Also, you have to provide a "forward declaration" in your Parent.h interface file that Child is a class like this:

@class Child;
@interface Parent
@property (strong, readonly) Child * child;
@end

Now your Parent class will know that Child is a class, too. But that is all it will know about Child. You will then need to import the Child.h file at the top of your Parent.m file so that your Parent class knows how to fully interact with the Child class.

For the Child class you should do something similar in its .h file so that Child will know that Parent is a class:

@class Parent;
@interface Child
@property (weak, readonly) Parent * parent;
@end

Also, you should import the Parent.h interface file at the top of the Child.m implementation file so that the Child class knows how to interact with the Parent class.

You might also read up on "delegates" and "protocols" to see if that might be a better way to handle all of this. I think that having a subordinate class import the .h interface file of the class above it in hierarchy is often not considered to be a good programming practice.

Samuel W.
  • 370
  • 1
  • 10
  • That's not what I'm asking. I'm aware of forward declarations what I'm asking *really* is about the initialisation and the properties being read only and, no, they don't inherit from each other! Give me a second for an edit. – André Fratelli Jul 04 '15 at 23:00