简体   繁体   中英

How to correctly notify a delegate that the instance is no longer needed?

This is my pattern:

1) SpecialView creates a MessageView and holds a strong reference to it.

2) User taps a button in MessageView which causes it to fade out. MessageView then tells it's delegate, SpecialView, that it faded out completely.

3) SpecialView releases MessageView.

The problem is this:

- (void)fadedOut:(NSString*)animationID finished:(NSNumber*)finished context:(void*)context {
    [self.delegate messageViewFadedOut:self]; // delegate releases us...
    // self maybe got deallocated... BOOM!
    // now what? method of a zombie returns? stack freaks out?
} // does it even return?

In the last line I'm calling the delegate, which in turn immediately releases MessageView. -fadedOut:finished:context: is called by a core animation didStopSelector callback.

My fear is that the MessageView instance is going to be deallocated immediately before the -fadedOut:finished:context: fully returned, causing very nasty random bugs

Once upon a time, a old veteran programmer told me: "Never cut the branch on which you're sitting on."

So in order to make sure the instance survives until this method returns, I made a retain-autorlease-dance before calling the delegate:

- (void)fadedOut:(NSString*)animationID finished:(NSNumber*)finished context:(void*)context {
    //[[self retain] autorelease];
    [self.delegate messageViewFadedOut:self]; // delegate releases us...
}

However, under ARC, retain-autorelease dances are not allowed anymore (the migration tool won't allow it) and there seems to be no way to force the ARC system to do something like this.

So I came up with this strategy instead:

- (void)fadedOut:(NSString*)animationID finished:(NSNumber*)finished context:(void*)context {
    [self.delegate performSelector:@selector(messageViewFadedOut:) withObject:self afterDelay:0];
}

The delayed performSelector hopefully lets the method fully return. As far as I know a delay of 0 still guarantees that the selector is performed in the next run loop iteration rather than immediately.

There's a good chance this is bogus as well.

How can I correctly resolve this problem of one object asking another to destroy the last reference to it with the chance that the object get's deallocated before the method that made the call to the other object has a chance to fully return? Can there be something like a stack trace zombie?

And how must I solve something like this under ARC?

To be honest, I think that rather than trying to emulate a retain-autorelease, you should make sure that by the time the delegate method messageViewFadedOut: is called you don't care if the owning reference to your message view is released. The contract for a willFadeOut: method may assume it won't be deallocated, but a fadedOut: or didFadeOut: method should be okay with the object being deallocated.

An ideal solution would be for your dealloc method to specifically avoid the nasty random bugs you are dreading, by cancelling any active fadeout animation that is occurring (hold a reference to it somewhere). That way you don't care how or when it gets to dealloc , because dealloc knows to do X to prevent leaving any object state or visual anomalies behind when it dies.

So just use your solution ( performSelector:withObject:afterDelay: ) or maybe a GCD block:

- (void)fadedOut:(NSString*)animationID finished:(NSNumber*)finished context:(void*)context
{
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 250 * NSEC_PER_MSEC), // 250ms
                   dispatch_get_current_queue(),
                   ^{
                        [self.delegate messageViewFadedOut:self];
                   });
}

Besides, ARC might just autorelease immediately, why don't you test that code section a bunch of times and see what happens? Put a breakpoint in dealloc and see if it's called before the method returns.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM