4

I'm an experienced C/C++ programmer starting to learn Objective-C development. I'm currently looking through the UICatalog sample and came across another instance of an idiom I've seen several places and never understood.

Code:

ButtonsViewController *buttonsViewController = [[ButtonsViewController alloc] initWithNibName:@"ButtonsViewController" bundle:nil];
[self.menuList addObject:[NSDictionary dictionaryWithObjectsAndKeys:
                                       NSLocalizedString(@"ButtonsTitle", @""), kTitleKey,
                                       NSLocalizedString(@"ButtonsExplain", @""), kExplainKey, 
                                       buttonsViewController, kViewControllerKey, nil]];
[buttonsViewController release];

AFAIK, this allocates and initializes a ButtonsViewController, creates an NSDictionary for the ButtonsViewController and adds the dictionary to an NSMutableArray called menuList (which is a member variable of the MainViewController where the above code lives) and then releases the buttonsViewController that it just created. Later, the MainViewController uses the dictionary entry to switch views to the buttonsViewController when necessary.

My question is: why is the buttonsViewController still valid after this code? It was allocated and released with no 'retains' between. Does adding something to an NSDictionary or NSMutableArray have an implicit 'retain'? If so, was I supposed to be able to figure that out somehow, or is it just one of those things you have to read and remember?

4 Answers4

4

It is standard procedure in the Cocoa frameworks for collections to retain the objects they contain. So yes, the mutable array is retaining the view controller.

This is always the case, unless the documentation says otherwise.

Dave DeLong
  • 242,470
  • 58
  • 448
  • 498
4

So, while it's true that the object is valid afterwards because the collection retains it, I'm not sure it's useful to think of it that way. If you intend to use the object in that method after adding it to the collection, you shouldn't release it until you're done with it.

The way to think about it is this:

1) I have a claim of ownership of this object I created

2) Now the collection and I both have ownership of this object

3) Now just the collection does, and I shouldn't use the object because I don't have ownership of it

and ignore the fact that you can technically get away with not paying attention to 3 in this case.

This allows you to keep using the NARC (new-alloc-retain-copy) rule for thinking about what retains objects.

Catfish_Man
  • 41,261
  • 11
  • 67
  • 84
  • 1
    I've switched over to thinking about ownerships rather than method names. If I directly own a collection, and it directly owns the objects I put in it, I treat that as my *indirect* ownership of all of the objects in the collection, without *directly* owning them at the same time. My argument for this is it's one fewer thing to remember to clean up; I welcome arguments against. – Peter Hosey Jan 19 '11 at 05:12
  • I would say that that model ends up being more complicated due to having to track which objects actually take ownership of things you hand them. Things that copy their args don't, weak collections don't, delegates don't, etc... these are all intuitive to you and me, but it's a lot concepts to learn for a new Cocoa programmer. The *local* ownership model "I own this while I use it" doesn't require thinking about other objects. – Catfish_Man Jan 19 '11 at 05:16
  • 1
    More succinctly, indirect ownership models promote coupling. – Catfish_Man Jan 19 '11 at 05:20
  • @Catfish_Man +1 as a general rule, any time you go more than one method call deep to access an object, you've gone too far. As a general rule. :) – Dave DeLong Jan 19 '11 at 05:34
  • @Catfish_Man: How does it promote coupling/violate LoD to add an object to my own array? The array is only there so that I can own more than one of the object at a time. I could see your argument for an NSMutableArray property, but that's why I don't do that—I have it expose an array property with mutative accessors instead (as recommended by the Model Object Programming Guide). Any other object must go through those accessors and may take co-ownership of any object(s) it finds in the array in parallel to the indirect ownership (through the array) by the array's owner. – Peter Hosey Jan 20 '11 at 05:43
  • I think we covered this on twitter after I posted that. Evan and I were misinterpreting what you were saying. – Catfish_Man Jan 20 '11 at 07:28
3

As mentioned, NSDictionary and NSArray send retain messages to objects added and also send release to objects when removed. The code above is conceptually transferring ownership of buttonsViewController to the dictionary. An example of how you might know this... from the documentation :

-addObject:forKey:

Parameters: anObject - The value for key. The object receives a retain message before being added to the dictionary. This value must not be nil.

-removeAllObjects

Discussion: Each key and corresponding value object is sent a release message.

Matt Wilding
  • 20,115
  • 3
  • 67
  • 95
1

Yes, containers like NSDictionary and NSMutableArray retain (or copy, in the case of dictionary keys) the objects you put into them.

See the Collections Programming Topics guide from Apple.

David Gelhar
  • 27,873
  • 3
  • 67
  • 84