user1178952
user1178952

Reputation:

Child losing its weak reference to parent

I have an AMContact object where one of its strong properties is an array of AMEmailAddress objects. One contact can have many email addresses. Can I create a strong property on my email address object that points to the contact object?

I feel like if its a strong reference there could be a retain cycle. If I make it weak, when I do my query to get all of the email address objects, at some point the contact object for each one is becoming nil.

- (NSArray*)allEmailAddresses
{
    NSArray *allContacts = [self allContacts];
    NSMutableArray *emailAddresses = [NSMutableArray array];

    for (AMContact *contact in allContacts) {
        if (contact.emailAddresses) {
            for (AMEmailAddress *address in contact.emailAddresses) {
                [emailAddresses addObject:address];
            }
        }
    }
    if (emailAddresses.count > 0) {
        return emailAddresses;
    }
    return nil;
} 


@interface AMContact : NSObject

@property (nonatomic, strong) NSString *firstName;
@property (nonatomic, strong) NSString *lastName;


// arrays of AMEmailAddress, AMPhoneNumber objects
@property (nonatomic, strong) NSArray *emailAddresses;
@property (nonatomic, strong) NSArray *phoneNumbers;

@end

@interface AMEmailAddress : NSObject

@property (nonatomic, strong) NSString *label;
@property (nonatomic, strong) NSString *email;
@property (nonatomic, strong) AMContact *contact; // IS THIS OK OR A RETAIN CYCLE?

@end

Upvotes: 0

Views: 517

Answers (2)

Jai Govindani
Jai Govindani

Reputation: 3211

A short and simple answer to your question: Can I create a strong property on my email address object that points to the contact object?

Can you? Yes. Should you? Absolutely not. Your model design already hints at what the relationship should be - the AMContact objects owns the email addresses, not the other way around. Having email addresses have a strong reference to the parent object is not a good way to go about this. The reference from child back to owner should be weak. The fact that your AMContact instance is going to nil is an issue with scoping and design and you shouldn't use a strong reference to get around this.

As for why your AMContact instance is going nil - It's a bit odd because if the AMContact object goes to nil, its child objects should as well. Is it going nil in the code you posted, or at some other point in some other method? Seeing the code where you're trying to refer to it, and it's nil, would be good.

UPDATE:

If you want a list of contacts that have email addresses, you should change the method to return that. However I'm guessing what you mean is that you want a list of their email addresses with their associated contacts. In this case you would have to flip the relationship around and consider the email to be the parent, and the contact to be the child. Note that if a contact has multiple email addresses then you'll have multiple references to the same contact. Assuming you have a contact property of type AMContact in your AMEmailAddress object, I would add a line as follows:

for (AMEmailAddress *address in contact.emailAddresses) {

            //Assign the contact as a property of the email address
            //Make sure the contact property is strong
            address.contact = contact;

            [emailAddresses addObject:address];
        }

 //Nil out the emailAddresses array
 contact.emailAddresses = nil;

UPDATE:

Ok so now you should have a flipped object - emailAddress as the parent and contact as the child, with no strong reference from contact to emailAddress anymore. This means you won't be able to re-use the contact object to get email addresses again though. If you need to maintain the original contact object as-is then you're right, you'll have to store it as a property in the View Controller or in another class - for approaches like this where I have to deal with people objects (for allowing users to invite friends, etc) I use a 'People Manager' singleton and keep all the original objects there.

Upvotes: 1

Dmitry Shevchenko
Dmitry Shevchenko

Reputation: 32414

Well in your case, I think it's very clear that Contact is the owner (parent) of his email addresses, so you will avoid any cycles by just designing this relation in that way, i.e. using weak reference back to contact. If you use a strong ref, then you will have a retain cycle because NSArray retains it's children in emailAdresses.

Now, I would check your program structure to see how and why you lose contacts in that weak reference, something must be releasing them, perhaps, your contact leaves scope prematurely?

You can get more info and advice in this great article on retain cycles, there are some workarounds as well, but I would encourage you to stick to a design that makes more sense.

Upvotes: 0

Related Questions