user789327
user789327

Reputation:

Objective-C pointers/memory management question

I am testing the following code below. ffv is declared in the interface file.

ffv = [[FullFunctionView alloc] initWithFrame:self.view.bounds];
NSLog(@"%i", [ffv retainCount]);  // prints 1
[self.view insertSubview:ffv belowSubview:switchViewsBtn];
NSLog(@"%i", [ffv retainCount]);  // prints 2
[ffv release]; // you can release it now since the view has ownership of ffv
NSLog(@"%i", [ffv retainCount]);  // prints 1

if (ffv == nil)
    NSLog(@"ffv is nil");

// "ffv is nil" is not printed

[ffv testMethod]; // "test method called" is printed

this is my [ffv testMethod] implementation

- (void)testMethod
{
    NSLog(@"test method called");
}

What I deduce in this case is that even if you release an object with retain count 2, you lose ownership of that object however, the reference is still kept.

Now, my question are:

  1. Is my deduction correct?
  2. Is there anything else important that can be deduced from this?
  3. What are the complications caused by still keeping (using) ffv and calling methods from ffv? (My opinion is that this is ok since the view will always own ffv and won't release it until someone calls viewDidUnload. And as long as I don't pass ffv's reference to other objects.)

Upvotes: 1

Views: 257

Answers (5)

iandotkelly
iandotkelly

Reputation: 9124

Well, I'm not sure 'losing' ownership is the right term. In Objective-C you have to carefully marshal your ownership of the object. If you create or retain an object, you are responsible for releasing it (either directly or via an autorelease pool). When you call release however, you don't lose a reference to the object, if something else has retained it, it will still be in memory, and your pointer will still potentially point to it.

You have a pointer ffv which is just a pointer to some memory, and you have the object which is created in the first line that ffv points to.

By calling release, you are stating that you no longer require the ponter ffv to point to a valid object, that in this context you would be happy for the object to be deallocated. The pointer still points to that bit of memory, and it is still there because its retain count was increased by assigning it to the view.

The line [ffv testMethod] is in danger of not working, as it follows the release and may not point to a valid object. It only works because something else is keeping it alive. ffv still has the same address value that it had when it was first assigned.

So in order:

  1. Your deduction is correct.
  2. Not really.
  3. You shouldn't use ffv after the release call. You have no guarantee that the object is going to be there for you.

These are pointers we are using here, not references like you find in Java or C#. You have to marshal your ownership of the object, you create it, have some pointers to it and by careful management of retain and release calls you keep it in memory for as long as you need it.

Upvotes: 0

cduhn
cduhn

Reputation: 17918

1) Is my deduction correct?

Your deduction is correct. The Memory Management Programming Guide explains that each object has one or many owners. You own any object you create using any method starting with alloc, new, copy, or mutableCopy. You can also take ownership of an object using retain. When you're done with an object, you must relinquish ownership using release or autorelease.

Releasing the object doesn't change the value of any variables that reference that object. Your variable contains the object's memory address until you reassign it, no matter what retain count the object has. Even if the object's retain count goes to zero, causing the object to get deallocated, your variable will still point at that same address. If you try to access the object after it's been deallocated, your app will normally crash with EXC_BAD_ACCESS. This is a common memory management bug.

2) Is there anything else important that can be deduced from this?

Nothing comes to mind.

3) What are the complications caused by still keeping (using) ffv and calling methods from ffv? (My opinion is that this is ok since the view will always own ffv and won't release it until someone calls viewDidUnload. And as long as I don't pass ffv's reference to other objects.)

When you call release, you are telling the Objective C runtime that you no longer require access to the object. While there may be many cases like this one in which you know the object will still exist, in practice you really shouldn't access an object after calling release. You'd just be tempting fate and setting yourself up for future bugs.

I personally don't like peppering my code with release statements, because I don't trust myself to remember them 100% of the time. Instead, I prefer to autorelease my variables as soon as I allocate them like this:

ffv = [[[FullFunctionView alloc] initWithFrame:self.view.bounds] autorelease];

This guarantees that ffv will exist at least until the end of the method. It will get released shortly thereafter, typically before the next iteration of the run loop. (In theory this could consume excessive memory if you're allocating a large number of temporary objects in a tight loop, but in practice I've never encountered this case. If I ever do, it will be easy to optimize.)

Upvotes: 0

filipe
filipe

Reputation: 3380

if (ffv == nil)
    NSLog(@"ffv is nil");
// "ffv is nil" is not printed

That's correct, releasing an object does not set the pointer to nil even if it is dealloced at that time. Good practice is to always set your pointer to nil after you release it. You are correct to say that after you released it, it wasn't dealloced because the view still had a retain on it. But that's not how you should be thinking about it. If you want to use that object and you want it to be alive, retain it. Doesn't matter who else is retaining it. Your object has nothing to do with those other objects. You want it, retain it. You're done with it, release it and set your pointers to nil. If you don't set it to nil and everyone else also released it, you will have a dangling pointer to an object that was dealloced, and that will cause you a crash and much grievance.

so this:

[ffv release]; // you can release it now since the view has ownership of ffv
ffv = nil;  // you released it, so that means you don't want it anymore, so set the pointer to nil

if you still want to use it, don't release it until you're done with it.

Upvotes: 0

omz
omz

Reputation: 53551

There are a couple of problems with using ffv after you have released it and it's only retained by your view controller's view.

1) It introduces a potential for future bugs, because later you might not remember that ffv is otherwise not retained. When you release the view (e.g. by replacing it with another view), you have a dangling pointer that you still hold a reference to.

2) In the special case of a UIViewController the view could be released at any time (you usually never call viewDidUnload yourself). The default behavior of UIViewController, when receiving a memory warning and the view is currently not visible, is to release the view, so unless you set the reference to nil in viewDidUnload, you have a dangling pointer again, even though you never explicitly released the view yourself.

Upvotes: 2

LaC
LaC

Reputation: 12824

The object is not deallocated until the retain count goes to 0. As long as it's not deallocated, you can keep using it without trouble. By retaining it you ensure that it won't be deallocated under your feet; however, if you retain another object that you know retains the first object, you can get away with this form of "indirect retaining". Don't complain when you move things around later and things start breaking, though.

Upvotes: 0

Related Questions