Question

Let's say I do the following (foo starts out as some object):

[foo release];
// STUFF with foo   
[foo retain];

As long as I'm not re-assigning foo, at the end of this I'll still have the same foo, right? I'm not risking losing foo during the //STUFF am I?

I guess I'm making sure that my understanding of release is correct. If I release foo, it doesn't actually go away until all handles on it are gone. In other words, foo would have to be assigned to some other object in the //STUFF, or foo would have to go out of scope in the //STUFF (and presumably have a new foo created) in order for the actual original foo object to be deleted, right?

EDIT for motivation:

The reason I want to do this is that lets say I have the following switch statement:

switch (test)
{
   case 1:
      foo = [A alloc];
      [foo inita];
      break;
   case 2:
      foo =  [B alloc];
      [foo initb];
      break;
   case 3: 
      [foo setupc];
      break;
   case 4:
      f = [D alloc];
      [foo initd];
      break;
}

It makes sense to release foo before the switch and retain it at then end. EXCEPT for case 3. So, I was thinking that if was safe to do what I proposed, it might make the code simpler.

Of course I can just put a release/retain pair around each alloc/init, but that's a lot of replicated code...

A [foo autorelease] and then the retain might just do the trick.

Was it helpful?

Solution

Nope, here's what happens:

The -release method decrements the retain count and then checks to see if it's now zero. If the retain count is zero, -release calls [self dealloc], which causes the object to be deallocated immediately. So sending -release before sending a -retain message would not be a good idea given your example, and would be likely to crash your app.

Based on your added comments, here's an alternative way to write the code that I think will do what you want while avoiding code duplication:

Class class = Nil;

// Decide which class (if any) to use...
switch (test)
{
    case 1: class = [A class];  break;
    case 2: class = [B class];  break;
    case 3: class = foo == nil ? Nil : [C class]; break;
    case 4: class = [D class];  break;
}

// If a class was selected, create a new instance 
// and release the previous one...
if (class != Nil)
{
    [foo release];
    foo = [[class alloc] init];
}

Note that there's no need for a -retain here because as I mentioned previously, +alloc sets the retain count to one.

OTHER TIPS

If the retain count of foo goes to zero at the beginning of that code, it will get obliterated and stop working. Use autorelease if you want to do something like that. release is a no-op in garbage collected environements, according to the docs - maybe that's what you're thinking of?

All release does is decrement the reference counter, which every object has.

I'm not sure why you'd want to release and retain like you've shown, though.

Yes, you most definitely are risking "loosing" during STUFF in a reference-counted (non-GC) environment. If your first -release decrements foo's reference count to 0, it will be deallocated. Continuing to use foo in this case is venturing into undefined behavior, and you will almost certainly pay the price eventually. In other words, there be dragons here. It might work in the case that you (and any other frameworks you call) don't allocate any memory during STUFF which overrites the allocated instance that foo referenced and the instance pointed to by foo's -dealloc method doesn't change foo's state except for releasing instance variable reference and smashes the memory occupied by those references and so on. In this case, your code might work as if foo hadn't been deallocated, but that's only luck.

In a garbage-collected environment, you're safe. Because you hold a reference to foo through STUFF, and because -release and -retain are no-ops in a GC environment, foo will still be valid.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top