On Apr 18, 2013, at 11:01 PM, Oleg Krupnov <oleg.krup...@gmail.com> wrote:
> I recently encountered an alarming problem that I have never been
> aware of, and I'd like to ask about the best practice to handle it.
> 
> Is it true that any object (let's call it Controller) that owns an
> NSTimer, scheduled to fire a method (let's call it -timerDidFire) of
> the same Controller, creates a circular reference and causes the
> Controller to never be released and the timer never stop triggering?
> 
> It looks like scheduling the timer causes the run loop to retain a
> reference to the object that implements -timerDidFire, in this case
> it's the Controller. Is this true? Then this is a circular reference,
> right?
> 
> In this case, if [_timer invalidate]; is called only in Controller's
> dealloc, then the timer never stops firing?
> 
> Which is the best way to prevent this problem? Currently I'm thinking
> about some kind of -[controller disconnect]; method that I need to
> call before [controller release]; in controller's parent object, but
> that seems a bit unwieldy solution.
> 
> Any other ideas/considerations? Thanks!

Yes, NSTimer retains its target. If the timer is a repeating one, then 
something other than the timer target's -dealloc method needs to invalidate the 
timer.

A -disconnect or -invalidate or responsible parent controller is one solution.

Another solution is to introduce a weak reference between the timer and your 
controller. Create a helper class that holds a weak reference to your 
Controller object. The helper object implements nothing except to respond to 
the timer method by calling the controller's method. Schedule the timer 
targeting the helper object. 

Now the timer no longer retains the controller, so -[Controller dealloc] will 
run and it can invalidate the timer. When the timer is invalidated it will in 
turn release the helper object, which then ought to become deallocated because 
there's no need to retain it anywhere else.

If you use an ARC __weak variable instead of an ordinary non-retaining 
variable, it will also solve an otherwise thorny multithreading race. The bug 
occurs when the timer fires after -[Controller dealloc] has started on another 
thread but before -[Controller dealloc] is able to unregister the timer. This 
means the timer method is running on a partially-destroyed controller object, 
which is unlikely to work well. Using a __weak variable fixes it because the 
__weak variable becomes nil precisely when the controller object's retain count 
becomes zero. Either the helper object sends the message to a valid and 
strongly-referenced Controller, or the helper object sends a message to nil 
which does nothing. The controller's timer method and -dealloc method won't 
interfere with each other.


-- 
Greg Parker     gpar...@apple.com     Runtime Wrangler



_______________________________________________

Cocoa-dev mailing list (Cocoa-dev@lists.apple.com)

Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com

Help/Unsubscribe/Update your Subscription:
https://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com

This email sent to arch...@mail-archive.com

Reply via email to