0
votes

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.

4
It would help if you'd provide a little more context here. Is foo an instance variable and are you assuming that foo already contains an object before the switch? (Otherwise, why would you need to call -release?) Also, alloc returns an instance with a retain count of 1. Why do you need the extra call to retain? I'm not at all clear on what you're trying to do here.jlehr
What is the nature of the test? Depending on the answer, it might be possibly to replace the switch with polymorphic behavior.outis
@JLehr, test is an int based on a mimetype. the different types are different types of documents... foo is an instance variable, and it may already contain an object. (hence the release) In the actual code in case 3 theres a "if (foo==nil) foo = [C alloc];" in case 3... I'm not exactly sure whether I need the retain or not. I guess that my question is bigger than I thought...Brian Postow

4 Answers

3
votes

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?

3
votes

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.

0
votes

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.

0
votes

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.