views:

122

answers:

5

Hi,

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

+2  A: 

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

vodkhang
@vodkhang, I got your point, but I think it's all about coding style, I really feel insecure when I see fuctions return locally allocated object pointers, and there mostly should be two kinds of varialbes: member variable or temporary variable, no others; the purpose of "operateOnTheButton" is to modify the input button... so yes, it's fine if you give me a button, and I return with another button... IMHO
shader
@shader that might be true for C++, but in Objective-C it is normal for a variable to have multiple owners. Returning locally allocated objects is also normal because Objective-C has `autorelease`, and C++ doesn't.
Tom Dalling
OK Tom, you got me, When you are in Rome, do the Romans do, I will then take your coding style. Until now our focus is all on 2nd case in [4], so if I return a locally allocated object pointer and autorelease it, all my instructions are fine? :)
shader
@shader: it is not just about coding style. It is also about the rule of memory management, you don't create it, who allows you to release it. That's what Apple strongly recommends and I think you should follow. And as Tom Dalling said, we also had autorelease
vodkhang
@shader: are you talking about this?-(UIButton*) getTheButton() { UIButton* myButton = [[UIButton alloc] init]; return myButton; //TERRIBLE!}
vodkhang
As vodkhang said, it's not really a matter of style, it's stems from the fact that C++ and ObjC have fundamentally different memory management models. But, to answer your question, yes all you have to do when returning a locally allocated object is autorelease it. If the caller wants to keep the return value it can use a retain, otherwise the return value will be deallocated in the very near future.
Tom Dalling
Tom, thanks, but I still don't get when should I return a pointer to a locally allocated object, I don't see necessity for that. Will you show me an example?
shader
Do you know about factory method? A really good and usual use case is: to use with singleton and reuse some memory like if you want only 1 user default manager in your system, you can do factory method : [NSUserDefault userDefault]; It will not allow you to do alloc init
vodkhang
@vodkhang, so you mean I might allocate a singleton within a method and return its pointer to others, so I must autorelease it? If so, that's a good example. Thanks.
shader
@Shader, examples abound in the foundation classes. Every data class like `NSString`, `NSArray`, `NSDictionary`, etc. have 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.
jasongetsdown
@jasongetsdown, I got your point, but if I don't want to write any code library for Cocoa Touch, I just want to call its API's, it seems I rarely have to write factory functions. You think?
shader
vodkhang, you might also want to mention that the last snippet leaks the new instance. As does your second snippet - you assign an object you own to a local variable and leak it subsequently.
Georg Fritzsche
ok, I added more in
vodkhang
+7  A: 

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.

jasongetsdown
Regarding point 4, ObjC code routinely returns member values that are pointer - think e.g. a `NSString*` property. The caller has the responsibility to manage ownership properly - if he violates the conventions its his fault.
Georg Fritzsche
The reasoning for point 4, example 3 is also off: It doesn't create a mem-leak on the incoming pointer. It releases an incoming object it doesn't own (leading to over-release later and thus some EXC_BAD_ACCESS) and then leaks a new instance.
Georg Fritzsche
That makes sense if your class is a model class perhaps, but you can hardly generalize, as shader has, that its always ok to do so. You are correct about the memory leak though. I'll edit my answer. Either way, you should release an object you don't own.
jasongetsdown
You can also not generalize that returning the value of a pointer member violates class integrity if every class-type getter implementation does exactly that. See e.g. the [getter examples](http://developer.apple.com/mac/library/documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmAccessorMethods.html) in Apples docs.
Georg Fritzsche
I think every @synthesizer returns member pointers in the get function it generates...
shader
Understood, but by his own admission that's not what he's talking about. I took to "someone will take care of it" to mean he's giving responsibility for that object to the caller, which implies that he doesn't understand the conventions of obj-c memory management. I was probably a little overenthused tho. I'll give you that :)
jasongetsdown
@shader, unless you specify copy. As long as you understand what an appropriate use for `autorelease` looks like, and you understand why your third code example contains an error (ty Georg), you're good. You may never have a reason to use `autorelease`, but if you do you should know it :) Excuse my lecturing...
jasongetsdown
@jasongetsdown, Yeah, you are right, but I won't specify copy on any iPhone applications. Please modify your answer a bit and I will close this topic :) Thanks for the help from all of you.
shader
Copy for synthesized getters [applies to assignment only](http://developer.apple.com/mac/library/documentation/Cocoa/Conceptual/ObjectiveC/Articles/ocProperties.html#//apple_ref/doc/uid/TP30001163-CH17-SW32): *"If you use the copy declaration attribute, you specify that a value is copied during assignment."*. There is no mention of the getter copying.
Georg Fritzsche
I was going to refute that but I forgot that copy makes an immutable copy by default. Modified...
jasongetsdown
@jasongetsdown, Will you add the part about factory function into the answer? I really like it. I was a C#/Java coder back in 2004, and I learned design pattern, but after that, I become a real-time graphics coder using c++ and shader/cuda, your comment reminds me of the old knowledge, please add it :)
shader
Right on. Java and the Obj-C framework definitely share a reverence for design patterns.
jasongetsdown
So factory funcitons produce objects and are responsible for their destruction, and we, the users, simply consume it. It's cool Objective C can achieve that with native performance, Java's virtual machine and C#'s .net framework mainly take "performance" out of the programmer's dictionary...
shader
That's a nice way to put it.
jasongetsdown
A: 

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.

jpmartineau
+1  A: 
  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.

Tom Dalling
The snippet will also lead to over-release of the incoming `button`.
Georg Fritzsche
A: 

“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

Objective Interested Person