iphone - Repeating NSTimer, weak reference, owning reference or iVar? -
i thought put out here separate question previous retaining-repeating-nstimer-for-later-access discussion has moved forward making new question clearer yet edit:
the scenario object creates repeating nstimer, lets in viewdidload, once created nstimer needs stay around can accessed other methods.
nstimer *ti = [nstimer scheduledtimerwithtimeinterval:1 target:self selector:@selector(updatedisplay:) userinfo:nil repeats:yes];
i understand when created runloop takes ownership of nstimer , stops, removes , releases nstimer when [ti invalidate];
called.
by virtue of fact need access nstimer in more 1 method need way hold reference future use, revised question is:
// (1) should nstimer held using owning reference (i.e.) @property(nonatomic, retain) nstimer *walktimer; [self setwalktimer: ti]; ... ... // cancel method [[self walktimer] invalidate; [self setwalktimer:nil]; ... ... // dealloc method [walktimer release]; [super dealloc];
.
// (2) should nstimer held using weak reference (i.e.) @property(nonatomic, assign) nstimer *walktimer; [self setwalktimer: ti]; ... ... // cancel method [[self walktimer] invalidate]; [self setwalktimer:nil]; ... ... // dealloc method [super dealloc];
.
// (3) use ivar , rely on runloop holding (i.e. retaining) timer nstimer *walktimer; nstimer *walktimer = [nstimer scheduledtimerwithtimeinterval:1 target:self selector:@selector(updatedisplay:) userinfo:nil repeats:yes]; ... ... // cancel method [walktimer invalidate]; walktimer = nil;
.
// (4) not listed above ...
i happy (1) (2) (3) or (4) lot of discussion regarding best has been written on other thread. there seem lot of conflicting answers hope more specific question focus on might best practice in situation.
edit:
as side note in apple nstimer class reference 4 out of 5 of sample code projects use nstimers assigned** retained property. here example of class reference examples show:
@property (nonatomic, retain) nstimer *updatetimer; updatetimer = [nstimer scheduledtimerwithtimeinterval:.01 target:self selector:@selector(updatecurrenttime) userinfo:p repeats:yes]; ... ... // cancel [updatetimer invalidate]; updatetimer = nil; ... ... // dealloc method [super dealloc]; [updatetimer release];
** should noted in examples apple assigning ivar directly , not using property setter.
edit
after giving more thought , finding important flaw in reasoning, i've come different conclusion:
it doesn't matter much, whether hold owning or non-owning reference timer need invalidate. matter of taste.
the deal breaker is, target of timer is:
if object creates timer target, managing object's lifetime becomes more fragile: cannot retain/release managed, instead need ensure client holds last reference object makes invalidate timer before disposes of it.
let me illustrate situation couple of sort-of-object-graphs:
- you start in state setup timer , set target:
setup of timer: yourobject owned someclientobject. in parallel exists current run-loop array of scheduledtimers. setuptimer method called upon yourobject http://a.yfrog.com/img616/8947/fqlc.png - the result following initial state:
initial state: in addition former state yourobject has reference (owned or not) worktimer, in turn owns yourobject. furthermore, worktimer owned run-loops scheduledtimers array. http://a.yfrog.com/img640/3444/acq.png - so you'll use object, when you're done , release it, you'll end with
simple release leak: after someclientobject disposes of yourobject through simple release, yourobject disassociated object-graph kept alive worktimer. worktimer , yourobject leaked! http://a.yfrog.com/img610/7223/jyyj.png
leak object (and timer) because runloop keeps timer alive, — in turn — keeps owning reference object.
this can avoided if yourobject ever owned one single instance @ time, when disposed of:
proper disposal through cancellation: before disposing of yourobject through release, someclientobject calls canceltimer method on yourobject. within method, yourobject invalidates worktimer , (if owned worktimer) disposes of worktimer through release http://a.yfrog.com/img614/7428/p8af.png
but now, how resolve following situation?
multiple owners: setup in initial state, multiple independent clientobjects hold references yourobject http://a.yfrog.com/img619/3908/wqe.png
there no easy answer, aware of! (not latter has much, but...)
so advice is...
- don't make timer property/don't provide accessors it! instead, keep private (with modern runtime think go far define ivar in class extension) , deal 1 single object. (you may retain it, if feel more comfortable doing so, there absolutely no need it.)
- caveat: if absolutely need access timer object, make property
retain
timer (as way avoid crashes caused clients directly invalidated timer accessed) and provide own setter. rescheduling timer — in opinion — not reason break encapsulation here: provide mutator if need that.
- caveat: if absolutely need access timer object, make property
- set timer target other self. (there plenty of ways doing so. maybe through writing generic
timertarget
class or — if can use — through mazeroingweakreference?)
i apologize being moron in first discussion , want thank daniel dickison , rob napier patience.
so here way going handle timers on:
// nstimer+d12weaktimertarget.h: #import <foundation/nstimer.h> @interface nstimer (d12weaktimertarget) +(nstimer *)d12scheduledtimerwithtimeinterval:(nstimeinterval)ti weaktarget:(id)target selector:(sel)selector userinfo:(id)userinfo repeats:(bool)shouldrepeat logsdeallocation:(bool)shouldlogdealloc; @end // nstimer+d12weaktimertarget.m: #import "nstimer+d12weaktimertarget.h" @interface d12weaktimertarget : nsobject { __weak id weaktarget; sel selector; // logging purposes: bool logging; nsstring *targetdescription; } -(id)initwithtarget:(id)target selector:(sel)aselector shouldlog:(bool)shouldlogdealloc; -(void)passthroughfiredtimer:(nstimer *)atimer; -(void)dumbcallbacktimer:(nstimer *)atimer; @end @implementation d12weaktimertarget -(id)initwithtarget:(id)target selector:(sel)aselector shouldlog:(bool)shouldlogdealloc { self = [super init]; if ( !self ) return nil; logging = shouldlogdealloc; if (logging) targetdescription = [[target description] copy]; weaktarget = target; selector = aselector; return self; } -(void)dealloc { if (logging) nslog(@"-[%@ dealloc]! (target %@)", self, targetdescription); [targetdescription release]; [super dealloc]; } -(void)passthroughfiredtimer:(nstimer *)atimer; { [weaktarget performselector:selector withobject:atimer]; } -(void)dumbcallbacktimer:(nstimer *)atimer; { [weaktarget performselector:selector]; } @end @implementation nstimer (d12weaktimertarget) +(nstimer *)d12scheduledtimerwithtimeinterval:(nstimeinterval)ti weaktarget:(id)target selector:(sel)selector userinfo:(id)userinfo repeats:(bool)shouldrepeat logsdeallocation:(bool)shouldlogdealloc { sel actualselector = @selector(dumbcallbacktimer:); if ( 2 != [[target methodsignatureforselector:aselector] numberofarguments] ) actualselector = @selector(passthroughfiredtimer:); d12weaktimertarget *indirector = [[d12weaktimertarget alloc] initwithtarget:target selector:selector shouldlog:shouldlogdealloc]; nstimer *thetimer = [nstimer scheduledtimerwithtimeinterval:ti target:indirector selector:actualselector userinfo:userinfo repeats:shouldrepeat]; [indirector release]; return thetimer; } @end
original (for full disclosure):
you know opinion your other post:
there little reason owning reference of scheduled timer (and bbum seems agree).
that said, options 2 , 3 same. (there additional messaging involved in [self setwalktimer:nil]
on walktimer = nil
i'm not sure if compiler won't optimize away , access ivar directly, well...)
Comments
Post a Comment