Iphone 在UIViewController内使NSTimer无效以避免保留周期的最佳时间
是否有人知道何时是停止UIViewController内的NSTimer的最佳时间,以避免保留计时器和控制器之间的循环 这里有一个更详细的问题:我在UIViewController中有一个NSTimer 在视图控制器的VIEWDID加载期间,我启动计时器:Iphone 在UIViewController内使NSTimer无效以避免保留周期的最佳时间,iphone,objective-c,memory-management,memory-leaks,nstimer,Iphone,Objective C,Memory Management,Memory Leaks,Nstimer,是否有人知道何时是停止UIViewController内的NSTimer的最佳时间,以避免保留计时器和控制器之间的循环 这里有一个更详细的问题:我在UIViewController中有一个NSTimer 在视图控制器的VIEWDID加载期间,我启动计时器: statusTimer = [NSTimer scheduledTimerWithTimeInterval: 1 target: self selector: @selector(updateStatus) userInfo: nil rep
statusTimer = [NSTimer scheduledTimerWithTimeInterval: 1 target: self selector: @selector(updateStatus) userInfo: nil repeats: YES];
上述操作会导致计时器保留对视图控制器的引用
现在我想释放我的控制器(例如,父控制器释放它)
问题是:我在哪里可以调用[statusTimer invalidate]来强制计时器释放对控制器的引用
我试着把它放在ViewDidUnload中,但是在视图收到内存警告之前,它不会被触发,所以这不是一个好地方。我尝试了dealloc,但是只要计时器还活着,dealloc就永远不会被调用(鸡和蛋的问题)
有什么好的建议吗?这个方法可能正是你想要的。每当视图被隐藏或取消时,都会调用它。您可以尝试使用-(void)viewdide消失:(BOOL)animated
,然后应该在-(void)viewdide出现:(BOOL)animated
中再次验证它
正是出于这个原因,我编写了一个“弱引用”类。它将NSObject子类化,但将NSObject不支持的所有方法转发给目标对象。计时器保留weakref,但weakref不保留其目标,因此没有保留周期
目标在dealoc中调用[weakref clear]和[timer invalidate]左右。讨厌,不是吗
(下一件显而易见的事情是编写自己的计时器类,为您处理所有这些。)
StatusUpdate
对象,或者通过使用控制器的指针初始化的StatusUpdater
来避免保留循环,持有对该对象的弱引用,并为您设置计时器
- 当目标窗口为
时,您可以让视图在nil
中停止计时器(该窗口应处理您提供的-willMoveToWindow:
反例)以及在-viewdide:
中停止计时器。这意味着您的视图将回到控制器中;如果您愿意,只需向控制器发送一条-viewdide:
消息或发布一条通知,就可以避免伸手去抢计时器-view:willMoveToWindow:
- 可能是您导致视图从窗口中删除,因此您可以在逐出视图的行旁边添加一行来停止计时器
- 您可以使用不重复计时器。一旦开火,它就会失效。然后,您可以在回调中测试是否应创建新的非重复计时器,如果是,则创建它。不需要的保留周期将只保留计时器和控制器对,直到下一个点火日期。有了一个1秒的约会,你就不用担心太多了
除了第一条建议外,每一条建议都是一种适应保留周期并在适当的时候打破它的方法。第一个建议实际上避免了保留周期。您可以在view controller的dealloc函数中编写此代码 例如
-(void)dealloc
{
if([statusTimer isValid])
{
[statusTimer inValidate];
[statustimer release];
statusTimer = nil;
}
}
这样,statustimer的参考计数器将自动递减1
&此外,分配内存上的数据也将被擦除
您也可以在
-(void)viewdiddemouse:(BOOL)animated
函数中编写此代码。我遇到了完全相同的问题,最后我决定重写视图控制器的释放方法,以查找重新计数为2且计时器正在运行的特殊情况。如果计时器没有运行,那么这将导致释放计数降至零,然后调用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];
}
我更喜欢这种方法,因为它使它保持简单并封装在一个对象中,即视图控制器中,因此更易于调试。然而,我不喜欢在保留/释放链上胡闹,但我找不到解决这个问题的方法
希望这对你有所帮助,如果你真的找到了更好的方法,我也很乐意听到
戴夫
编辑:应该是-(单向无效)一种解决方法是使NStimer保留对UIViewController的弱引用。我创建了一个类,该类包含对对象的弱引用,并将调用转发给该类:
#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
您的dealloc方法将被调用(与以前不同),您只需在其中调用:
[statusTimer invalidate];
如果timer.REPEAT设置为
YES
,则计时器的所有者(例如视图控制器或视图)将不会解除分配,直到计时器失效
这个问题的解决办法是找到一些触发点来停止计时器。
例如,我启动计时器在视图中播放动画GIF图像,触发点为:
UIView
的willMoveToWindow:
方法:
- (void)willMoveToWindow:(UIWindow *)newWindow {
if (self.animatedImages && newWindow) {
_animationTimer = [NSTimer scheduledTimerWithTimeInterval:_animationInterval
target:self selector:@selector(drawAnimationImages)
userInfo:nil repeats:YES];
} else {
[_animationTimer invalidate];
_animationTimer = nil;
}
}
如果您的计时器为ViewController所有,则可能会出现视图:
和视图将消失:
是启动和停止计时器的好地方。在内部使计时器无效-(void)viewwilldefine:(BOOL)动画确实对我有用适用于@可用(iOS 10.0,*)您还可以使用:
Timer.scheduledTimer(
withTimeInterval: 1,
repeats: true,
block: { [weak self] _ in
self?.updateStatus()
}
)
但是,当我使用:[self.view removeFromSuperview]隐藏父视图时,从未调用ViewDidEvale。还有其他建议吗?此外,当在UINavigationController中时,不会调用ViewDidEnglish,因此我正在寻找一个更一般的位置来放置它。如果可以依赖始终调用ViewDidEnglish,ViewDidEnglish将是完美的,不幸的是,在UINavigationController中,ViewDidEnglish不是这种情况
Timer.scheduledTimer(
withTimeInterval: 1,
repeats: true,
block: { [weak self] _ in
self?.updateStatus()
}
)