2
votes

I have been using ojective c for almost a week, and I am mainly a c++ coder. After I read Apple's memory management guide, I try to bring my memory usage style in c++ into objective c... I tried to conclude these scenarios, I think I won't make memory mistake if I follow these instructions. Please kindly let me know if I am wrong :)

I will try not to use autorelease, personally speaking, by using autorelease, there might always be some redundent memory before certain auto release pool is drained. I will only use release, which make sure my application uses minimum memory at any time.

Another thing Apple says, which I describe with my own words, is: everytime I add an retain/alloc/copy, I should add a release somewhere.

Here are all the scenarios I conclude:

  1. In the same function: alloc an object, use it, and release it

  2. In the init function of a class, alloc an object, in the dealloc function of the class, release the object

  3. When it's necessary to own a pointer, one should retain an input pointer in a method of a class(let's say method A), and then release the pointer in the dealloc function of the class.

    I found that the timing of using retain in objective c is the same to the timing of using memcpy in c/c++, so I take retain as a "memory efficient copy"

    If the input retained pointer is to set to a member pointer variable, then one should release the member pointer first. So in case[3], alloc in init of the class is paired with release in method A, and the retain in method A is paired with the release in dealloc

  4. Return a pointer as return value. Honestly speaking I never do such things when I use c++. It's OK if to return a member pointer, coz someone will take care of it:

    -(UIButton*) getTheButton() {
        return theButton;
    }
    

    But it's really terrible to return a pointer to a locally allocated object:

    -(UIButton*) getTheButton() {
        UIButton* myButton = [[UIButton alloc] init];
        return myButton; //TERRIBLE!
    } 
    

    Someone might say I should use autorelease in that case, but I simply want to bypass that solution by using this: I will only return member pointers, or I will not return pointers, I will only operate on given input pointers.

    -void operateOnTheButton(UIButton* button) {
        [button release];
        button = [[UIButton alloc] init];
    } 
    

So, please kindly let me know if there is any problem if I follow the memory usage instructions above.

Thanks :D

5
Minor note: Your last snippet seems to be assuming that button is an in-out parameter. That is not the case, in-out would be (UIButton**)button.Georg Fritzsche
I try to bring my memory usage style in c++ into objective c. Then you will fail. Do not be prejudiced against things you are not familiar with.JeremyP

5 Answers

9
votes

There is one important scenario where you must use autorelease. If you alloc/retain/copy an object and then return it to some other code you own that object, but you can't release it because other code needs to use it (or you wouldn't have returned it).

In this situation you must take responsibility for the object's release by sending it an autorelease message, thereby ensuring it will eventually be released. This is exactly what foundation methods like NSString's stringWithString: do. You don't have to release a string object you get from this method because you didn't alloc/retain/copy it, but if the method hadn't autoreleased it for you it would survive the [pool drain] and become a memory leak.

Make sure you understand how reference counting works. release decrements an object's reference count. When the count reaches 0 the object is destroyed. alloc and copy create an object with a ref count of 1. retain increments the receiving object's ref count by 1. For each alloc/retain/copy there must be one release. 1 up, 1 down.

So when you return an object you created, passing control to the callee, you unbalance the 1 to 1 equation because you have no way to release it. That's why there's autorelease. It doesn't decrement the receiver's ref counter, so there's no danger it will be destroyed, but when the pool is drained each object in the pool gets one release message for each autorelease it has received, restoring the balance (unless an object was intentionally retained so it would survive the drain, in which case the callee would need to release it later).

As for point 4, your second example is fine if you autorelease, examples abound of this technique in the foundation classes. Every data class like NSString, NSArray, NSDictionary, etc. has methods that return locally allocated object pointers. They usually have the form thisWiththat:, like stringWithstring: or dictionaryWithContentsOfFile:. The latter would allocate a dictionary object, populate it with the contents of the file, autorelease it, and return a pointer to you. Its called a factory function. Very common pattern in obj-c, java too.

You're third code example will cause a runtime error later. You are releasing an object you don't own. The owner will not expect you to release it which will likely mean it will receive one too many release messages, causing a runtime error. If you didn't alloc/retain/copy it, you don't own it so you shouldn't release it.

2
votes

The autorelease part is incorrect. If you do a factory method, which doesn't start with alloc/retain/copy . You have to autorelease it. The caller doesn't create the object, so the caller can not and should not release it. So, if the caller wants to do something and make sure the objects there, caller has to retain them itself.

The good solution is :

-(UIButton*) getTheButton() {
    UIButton* myButton = [[UIButton alloc] init];
    return [myButton autorelease];  // if you don't do autorelease, memory will leak
} 

then in the caller:

- (void)caller {
  UIButton *button = [[self getTheButton] retain];
}

You can not do this:

-void operateOnTheButton(UIButton* button) {
    [button release];
    button = [[UIButton alloc] init];
} 

The reason: operateOnTheButton does not own the button, it shouldn't release the button. What happens if I send you a button and then after the calling, I have another button. The operateOnButton doesn't alloc/retain/copy the parameter. It has no right to release it. It can create some problems for you:

1/ EXEC_BAD_ACCESS: someone may still use it when you release inside a method that you don't own the object

2/ memory still leaks: release doesn't mean you delete the memory immediately. You just decrement the retainCount by 1. Which means if when passed the button object has retainCount of 2, releasing it will only make your retainCount 1, doesn't remove the memory of the object yet

3/ your button object is also leak: noone can make sure that your button object is released. You own it, so you have to release it yourself, otherwise, the button object is leak

1
votes
  1. This scenario is normal, but some (myself included) prefer to autorelease on the same line as alloc to avoid accidentally forgetting to release.

  2. init does not normally call alloc. When creating on object, alloc is called first, then init is called on the newly allocated instance.

  3. This scenario is pretty much correct. Retaining doesn't make a copy, however.

  4. In Objective-C objects are always referred to by their pointers, which is different to C++. If you're writing a method that returns an string then you return an NSString*, never the NSString struct itself.

The following snippet of code will actually cause a memory leak:

-(void) operateOnTheButton:(UIButton*)button {
    [button release];
    button = [[UIButton alloc] init];
} 

button is a local variable, and when it goes out of scope it will leak because it hasn't been released. I'm not quite sure what this operateOnTheButton: method is supposed to do.

0
votes

Why do you want to avoid AutoRelease? It's the way it should be done...

Also, some advice:

One error that a lot of people do on the iPhone is allocate memory in a UIViewController's viewDidLoad method and then release it in the dealloc method instead of the viewDidUnload method.

Always consider that a view can be loaded more than once.

0
votes

“In the init function of a class, alloc an object, in the dealloc function of the class, release the object”

First: there are no functions, being some part of a class. Yes, you can write C-functions. You can write C-functions operating on instances. But what you get if you write some of this -(foo)doSomethings (or even +(foo)doSomethings) is a message handler. And you use them by sending messages.

This is the reason why you can’t alloc an instance from within an -(id)init message: init wants to “talk” to the memory and the instance of the superclass within. As you didn’t alloc this space and functionality, you will either some wild memory location or (more probable) to nil.

On the other hand your way to release an object will release nothing at all. Because dealloc ist sent to the object when it will be destroyed. But as you don’t release it (as you want to do that inside dealloc), the object will never be placed on the list to “heavens gate”: it keeps an retain-count of 1 (or greater) until your application dies and kicks the objects allocated memory back to the free pool of the OS. The dealloc-message is not to be sent by you. The dealloc ist just for the memory management routines. (I suppose, this message is sent if the most super NSObject, the root of your instances parents and grandparents, will receive a release and it sees, that the retain count should go below 1.)

If you really want to have your memory management in Objective C, you will have to write your own frameworks; your own “C++ conforming Cocoa”. IMHO.

Greetings