1

I have code like this:

vector<SoundObject*> sounds ;

- (void) loadSound:(NSString*) name
  {
    SoundObject* so = [[[SoundObject alloc] init] load:name] ;
    if( so )
      sounds.push_back( so ) ;
  }

Ok, so I did a couple of things here (I may be very noob at this, just focus on memory leaks).

  1. Should I call [so retain] before pushing so into the array?
  2. It is possible for load to fail, and if it does, it returns nil.
    • If load did fail, do I have to call [self release] before returning from load?
jscs
  • 63,694
  • 13
  • 151
  • 195
bobobobo
  • 64,917
  • 62
  • 258
  • 363
  • 6
    Is there a reason why you're choosing an STL `vector` over an `NSMutableArray`? The native Foundation classes will handle memory ownership for you. – Ben Zotto Sep 08 '12 at 03:11
  • __Oh interesting__. I am using STL vectors wherever possible [for performance reasons](http://stackoverflow.com/a/4915303/111307). I kind of realize that I'm not going to get much of a performance gain by using objective-C objects inside a C++ container, but the purpose of this question is to __understand Objective-C reference counting__. Given that, can someone answer the 2 questions I have posted here? – bobobobo Sep 08 '12 at 03:13

2 Answers2

3

If you're writing new code, you should just use Automatic Reference Counting (ARC).

It will mitigate a lot of potential memory issues you'd encounter.

Additionally, I'd stick with Foundation collections, like NSArray, or NSMutableArray, rather than what seems to be a C++ vector.

NSMutableArray *_myMutableArray = [[NSMutableArray alloc] init];

- (void)loadSound:(NSString *)name
{
  SoundObject *so = [[SoundObject alloc] init];

  if( nil != so )
  {
    [so load:name];

    [_myMutableArray addObject:so];
  }
}
Josh
  • 12,448
  • 10
  • 74
  • 118
  • Sorry, I should have mentioned I don't want to use `NSMutableArray` here -- I want to understand `[retain]` and `[release]`. – bobobobo Sep 08 '12 at 03:15
  • 1
    @bobobobo: Well, regardless of whether or not you use a Foundation collection, you can still use ARC. They are not mutually exclusive. – Josh Sep 08 '12 at 03:17
  • Am I not using ARC? I thought I was. – bobobobo Sep 08 '12 at 03:18
  • 1
    @bobobobo: If you use ARC, you don't need to (and shouldn't!) call things like -retain, -release, -dealloc, etc. – Josh Sep 08 '12 at 03:20
  • 1
    Oh! I am using ARC. I see. If I try and type `[so retain]` the build actually fails. Very cool. – bobobobo Sep 08 '12 at 03:32
3

but the purpose of this question is to understand Objective-C reference counting

You are going to understand less of Cocoa memory management if you use vector rather than using NSArray. This is because methods that follow Cocoa memory management rules cooperate together in a nice way; but the methods of vector are not written with retains and releases, and so do not follow Cocoa manual reference counting rules. So unless you're using ARC, you're going to have to break memory management rules yourself to explicitly memory-manage it from the outside. This is terrible and will teach you wrong ideas.

The great thing about Cocoa memory management is that it is completely local. You retain and release only based on what you do in the function, and you never have to worry about what other functions do. That's what makes it possible to statically analyze memory management and implement ARC.

What do I mean? The basic rule of Cocoa memory management is that object arguments of a function are guaranteed to be valid (i.e. not deallocated) when a function is called, and there is no guarantee about it being valid any time after. That's it. So when you pass an argument to another function, as long as the object is valid when you pass it, that's all you need to worry about. You never need to "retain the object for the other function" because the other function cannot assume that the object is valid at any time after anyway.

This includes adding things to an NSArray:

Foo *obj = [[Foo alloc] init];
[myArray addObject:obj];
[obj release];

Since you no longer need obj after adding it, you are free to release it. You don't need to worry about anything else. Now, as other people said, this works because the array retains its elements. But you don't need to know that. NSArray methods follow the same rules as all other methods of objects. You don't need to know how NSArray or any other class works to do memory management right. You don't even need to know what type of object is myArray or what addObject: does. That method takes care of it -- if it needs to keep the object around for longer, it must (as part of the memory management rules) somehow retain it; if it does not need to keep it, it doesn't need to do anything. The point is, it doesn't matter to you.

Another example:

Foo *obj = [[Foo alloc] init];
[someObj performSelector:@selector(something:) withObject:obj afterDelay:5];
[obj release];

This does something asynchronous, so how can we release it? Won't it be invalid by the time it is used? No. Again, this all follows the same memory management rules. You don't need to worry about what that method does. If it needs to keep the object around somehow (and it does in this case), it must retain it (and it indeed does) and take care of releasing it, etc.

All this simplicity and beauty goes away if you try to force something like vector into the picture. You will have to retain things before putting them in, and release things every time an element is removed. It is terrible. You could write a wrapper class around vector so you can have all the terrible memory management logic in that file and others can use it as a normal Objective-C class; but then that would be pretty much like NSMutableArray.

newacct
  • 119,665
  • 29
  • 163
  • 224