1

Lets say I have a UIViewController. Inside of that UIViewController I have a UITextView. In the viewDidLoad I set UITextView's delegate to self. This means that I need to do textview.delegate = nil in the dealloc? Is this true? Can someone please explain in layman terms why is this so?

If I am misunderstanding the concept of "relasing UIViewController but not setting the subviews delegate to nil prior to calling the [super dealloc]" then can someone give me an example of this

A REAL WRONG EXAMPLE: (CLARIFICATION)

CreateGroupViewController * cgvc = [[CreateGroupViewController alloc] init];
    cgvc.delegate = self;
    UINavigationController * uinc = [[UINavigationController alloc] initWithRootViewController:cgvc];
    uinc.navigationBar.tintColor = [UIColor blackColor];
    uinc.modalPresentationStyle = UIModalPresentationFormSheet;
    [self presentModalViewController:uinc animated:YES];
    cgvc.delegate = nil;    
    [cgvc release];

My point being is that, it is not actually correct to say that every time you release an object it is good to set the delegate to nil. In this case I am showing a ModalViewController. However before the delegate is executed I already nulled the delegate and therefore it doesn't perform. Now how do you explain this?

aherlambang
  • 14,290
  • 50
  • 150
  • 253

3 Answers3

0

Yes, you should set the delegate to nil when you're done with it. This prevents the app from crashing if an attempt is made to invoke a method on the delegate object after it has been released. (Keep in mind that it is perfectly legal to send a message to a nil object).

More details at Should you set the delegate to nil in the class using the delegate or in the class itself.

Community
  • 1
  • 1
csano
  • 13,266
  • 2
  • 28
  • 45
  • so setting the delegate to nil should be done before I release it right? so I need to do viewcontroller.delegate = nil and then viewcontroller release. Correct? – aherlambang May 30 '11 at 06:07
  • I rarely see people doing this, so say I have a UITableView and usually people do tableView.delegate = self. But I rareley see people doing tableView.delegate = nil in their dealloc – aherlambang May 30 '11 at 06:14
  • Read through the answers/comments at the link I provided for more details on why you should do this. – csano May 30 '11 at 06:36
  • @Josh Caldwell - I don't think that's what @aherlambang is getting at. If you've got a.delegate = self, you want to make sure you set a.delegate to nil before releasing a, right? – csano May 30 '11 at 07:04
  • It's definitely possible that I've misunderstood the question (that's why I deleted my comment). However, `obj.ivar = nil; [obj release];` doesn't seem to make sense either, because either `obj` is about to be destroyed, in which case so will its ivars, or you're destroying an ivar over which you don't have ownership. – jscs May 30 '11 at 07:07
  • It doesn't make any difference whether that ivar points to `self`; the object either a) retained its delegate and should release it in `dealloc`, or b) didn't and it doesn't matter. In either case, you shouldn't be trying to manage another object's memory. – jscs May 30 '11 at 07:14
  • If the delegate object is called `Q`, the only case where setting `obj.delegate = nil;` makes sense is if `obj` is going to be around _after_ `Q` gets deallocated. If `Q` also _owns_ `obj`, then `obj` will be deallocated when `Q` is, and `obj.delegate = nil` is pointless. – jscs May 30 '11 at 07:29
  • @Josh My understanding is that deallocation order isn't guaranteed and that we shouldn't make any assumptions about that. Wouldn't you say that setting the delegate to nil is safer? Maybe not necessary in all cases, but safer? – csano May 30 '11 at 07:47
  • I am all for safety, but I am 99.9% certain that `release` goes like this (dummy code, obviously) `- (void) release { self->refCount--; if( 0 == self->refCount ){ [self dealloc]; } }` So, in the owning object's `dealloc`, `[ivar release]` goes right to `[ivar dealloc]` and the owner's `dealloc` doesn't complete until that release call comes back. The only other thing that could happen would be a pseudo-GC, where the ivar was just marked for later deallocation when its retain count hit 0. – jscs May 30 '11 at 08:26
  • The object storing the delegate might have put something in the runloop queue which might start executing after the delegate is valid... – Eiko May 30 '11 at 08:38
  • @Eiko: Unless I'm still confused, we're talking about the delegate owning the other object, not an object owning its delegate, but you're right in the latter case. – jscs May 30 '11 at 08:47
0

Yes, That's the right way to go about this. Once you set delegate to an object, you are actually assigning the delegate to that particular object and its the ideal way to set it as nil before you release it.

visakh7
  • 26,380
  • 8
  • 55
  • 69
  • another question is that setting the delegate of some object to nil doesn't have to be in the dealloc right? It's before I do a release on the object? I rarely see this practice – aherlambang May 30 '11 at 06:17
  • Yes its before you release the object. In your case i guess it has to be dealloc cause the the object for your view controller will be released by the dealloc – visakh7 May 30 '11 at 06:31
  • Setting an object to `nil` before releasing it is nonsensical. Calling `[nil release]` literally does nothing. – jscs May 30 '11 at 06:52
  • @Josh: I didnt mean setting object to nil before release. I said about setting delegate of an object to nil before releasing the object – visakh7 May 30 '11 at 07:00
  • That doesn't make sense either: `obj.ivar = nil; [obj release];`? The ivar is going to be destroyed when the object is deallocated. – jscs May 30 '11 at 07:05
  • It's clearing out the reference to the delegate object incase something else in your object tries to access the delegate after you've told it that you're done with it. Reference http://stackoverflow.com/questions/6118311/setting-delegate-to-nil-in-dealloc – visakh7 May 30 '11 at 07:18
  • Okay, I see what you're saying, but that doesn't apply in the case that the delegate _owns_ the object that it's delegating for. `A` creates `B`, sets `B.delegate = A`; in `A`'s `dealloc`, `B` is getting destroyed anyways. There's no point setting `B.delegate = nil` first. – jscs May 30 '11 at 07:25
0

UPDATE Addressing your snippet:

CreateGroupViewController * cgvc;
cgvc = [[CreateGroupViewController alloc] init];
// We now _own_ cgvc; we are responsible for its memory
cgvc.delegate = self;    // cgvc will send us some fun messages

cgvc.delegate = nil;     // Not necessary, because:
[cgvc release];          // cgvc no longer exists

The only case where this makes sense is if the object which is the delegate, call it A, is being deallocated before the object it was delegating for, call it B. This might happen if B is a text field, for example. The text field will stick around after A disappears, and we don't want B trying to send messages to deallocated A:

MyDelegateClass * del = [[MyDelegateClass alloc] init];
myLongLivedObjThatNeedsADelegate.delegate = del;

// Do stuff...

// Release delegate object without setting outlet to nil
[del release];

// ----
// Some time later, in myLongLivedObjectThatNeedsADelegate:
[delegate objShouldDoX:self];  // CRASH! delegate points to 
                               // deallocated instance

If you have an object in a variable, and you no longer need the object, but the variable might (accidentally) get used later, it is a safety measure to set the variable to nil after releasing the object.

Demonstration:

NSString * str1 = [[NSString alloc] init];
NSString * str2 = [[NSString alloc] init];
NSString * str3 = [[NSString alloc] init];

str1 = nil;       // We just leaked the object that was at str1;
                  // there is no way to release it.
[str1 release];   // Does nothing
[str1 length];    // Also does nothing

[str2 release];   // Release the object
[str2 length];    // CRASH! message sent to deallocated instance

[str3 release];
str3 = nil;      // A safety measure
[str3 length];   // Does nothing

When you have properties and synthesized setters, it gets a little bit more interesting. If you have a property:

@property (retain) NSString * myString;

for which you synthesize the setter, the setter will look something like this:

- (void) setMyString:(NSString *)newString {
    [newString retain];    // Take ownership of new object
    [myString release];    // Relinquish old object
    myString = newString;  // Rename new object
}

So, if you do self.myString = nil;, what happens is equivalent to the following:

[self setMyString:nil];
    // Inside setMyString: newString is nil
    [nil release];        // Does nothing
    [myString release];   // Relinquish old object
    myString = nil;       // Set myString to nil

You see that the synthesized property carries out the conventional "safety" practice automatically for you. This is why you may see people recommend setting properties to nil.

The last thing to note here is that it doesn't really matter whether you set an ivar to nil in dealloc, because that name isn't going to be used anymore. The object that owns it is being destroyed, so the "safety measure" does not apply. Therefore, in dealloc, you can simply release your ivars without worry.

jscs
  • 63,694
  • 13
  • 151
  • 195
  • It's possible that I've misunderstood the question. – jscs May 30 '11 at 07:06
  • clarify my A REAL WRONG EXAMPLE above... that's actually where got me confused – aherlambang May 30 '11 at 07:29
  • @aherlambang: Done; although the rest of my post does not directly address your question, I hope that it helps you understand the background. – jscs May 30 '11 at 07:34
  • @Josh you said that setting cgvc.delegate = nil before releasing has no effect.. I kind of disagree with that... in your explanation you are not adding that view controller as a rootviewcontroller in the navigation controller.. I think this makes a difference – aherlambang May 30 '11 at 14:49
  • What CreateGroupViewController has is a textbox and a button that sends a delegate message back to the parent view controller (the class that presents the modal view controller), so if I set the delegate to nil before releasing it and not setting it to nil before releasing it there are two different behavior.. If I set the delegate to nil, then the parent view controller won't get the message sent to it (as delegate is nil), if I don't set it to nil, then it works the way it's supposed to (the parent view controller gets the message being sent to it) – aherlambang May 30 '11 at 14:53
  • The only consideration is the relative lifetimes of the two objects: is the one that's sending messages to the delegate going to outlive its delegate? If yes, then it might try to send a message to an invalid object, so set the outlet to `nil`. – jscs May 30 '11 at 18:30