Best time to invalidate NSTimer inside UIViewContr

2019-01-17 02:58发布

Does any one know when is the best time to stop an NSTimer that is held reference inside of a UIViewController to avoid retain cycle between the timer and the controller?

Here is the question in more details: I have an NSTimer inside of a UIViewController.

During ViewDidLoad of the view controller, I start the timer:

statusTimer = [NSTimer scheduledTimerWithTimeInterval: 1 target: self selector: @selector(updateStatus) userInfo: nil repeats: YES];

The above causes the timer to hold a reference to the view controller.

Now I want to release my controller (parent controller releases it for example)

the question is: where can I put the call to [statusTimer invalidate] to force the timer to release the reference to the controller?

I tried putting it in ViewDidUnload, but that does not get fired until the view receives a memory warning, so not a good place. I tried dealloc, but dealloc will never get called as long as the timer is alive (chicken & egg problem).

Any good suggestions?

9条回答
Animai°情兽
2楼-- · 2019-01-17 03:32

You can try with - (void)viewDidDisappear:(BOOL)animated and then you should validate it again in - (void)viewDidAppear:(BOOL)animated

More here

查看更多
Summer. ? 凉城
3楼-- · 2019-01-17 03:34

One way around it is to make the NStimer hold a weak reference to your UIViewController. I created a class that holds a weak reference to your object and forwards the calls to that:

#import <Foundation/Foundation.h>

@interface WeakRefClass : NSObject

+ (id) getWeakReferenceOf: (id) source;

- (void)forwardInvocation:(NSInvocation *)anInvocation;

@property(nonatomic,assign) id source;

@end

@implementation WeakRefClass

@synthesize source;

- (id)init{
    self = [super init];
//    if (self) {
//    }
    return self;
}

+ (id) getWeakReferenceOf: (id) _source{

    WeakRefClass* ref = [[WeakRefClass alloc]init];
    ref.source = _source; //hold weak reference to original class

    return [ref autorelease];
}

- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
    return [[self.source class ] instanceMethodSignatureForSelector:aSelector];
}

- (void)forwardInvocation:(NSInvocation *)anInvocation
{
    [anInvocation    invokeWithTarget:self.source ];

}

@end

and you use it like this:

statusTimer = [NSTimer scheduledTimerWithTimeInterval: 1 target: [WeakRefClass getWeakReferenceOf:self] selector: @selector(updateStatus) userInfo: nil repeats: YES];

Your dealloc method gets called (unlike before) and inside it you just call:

[statusTimer invalidate];
查看更多
你好瞎i
4楼-- · 2019-01-17 03:35

I wrote a "weak reference" class for exactly this reason. It subclasses NSObject, but forwards all methods that NSObject doesn't support to a target object. The timer retains the weakref, but the weakref doesn't retain its target, so there's no retain cycle.

The target calls [weakref clear] and [timer invalidate] or so in dealloc. Icky, isn't it?

(The next obvious thing is to write your own timer class that handles all of this for you.)

查看更多
SAY GOODBYE
5楼-- · 2019-01-17 03:36

invalidate timer inside - (void)viewWillDisappear:(BOOL)animated did work for me

查看更多
在下西门庆
6楼-- · 2019-01-17 03:36

I had exactly the same issue and in the end I decided to override the release method of the View Controller to look for the special case of the retainCount being 2 and my timer running. If the timer wasn't running then this would have caused the release count to drop to zero and then call dealloc.

- (oneway void) release {
    // Check for special case where the only retain is from the timer
    if (bTimerRunning && [self retainCount] == 2) {
        bTimerRunning = NO;
        [gameLoopTimer invalidate];
    }
    [super release];
}

I prefer this approach because it keeps it simple and encapsulated within the one object, i.e., the View Controller and therefore easier to debug. I don't like, however, mucking about with the retain/release chain but I cannot find a way around this.

Hope this helps and if you do find a better approach would love to hear it too.

Dave

EDIT: Should have been -(oneway void)

查看更多
Animai°情兽
7楼-- · 2019-01-17 03:39

You can write this code in dealloc function of view controller

for eg.

-(void)dealloc
{
   if([statusTimer isValid])
  {
       [statusTimer inValidate];
       [statustimer release];
      statusTimer = nil;
  }
}

this way the reference counter of statustimer will automatically decrement by 1 & also the data on the allocated memory will also erase

also you can write this code in - (void)viewDidDisappear:(BOOL)animated function

查看更多
登录 后发表回答