我想我会把它作为我之前的一个单独的问题 retaining-repeating-nstimer-for-later-access随着讨论的推进,新的问题比另一个编辑更清晰:
该场景是一个对象创建一个重复的NSTimer,比如说在viewDidLoad中,一旦创建了NSTimer需要保持不变,所以可以通过其他方法访问它。
NSTimer *ti = [NSTimer scheduledTimerWithTimeInterval:1
target:self
selector:@selector(updateDisplay:)
userInfo:nil
repeats:YES];
据我所知,创建后,runloop将获得NSTimer的所有权,并在调用[ti invalidate];
时最终停止,删除和释放NSTimer。
由于我们需要以多种方法访问NSTimer,我们需要一些方法来保存参考以供将来使用,修订后的问题是:
// (1) Should the NSTimer be held using an 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 the NSTimer be held using a 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 an iVar and rely on the runLoop holding (i.e. retaining) the timer
NSTimer *walkTimer;
NSTimer *walkTimer = [NSTimer scheduledTimerWithTimeInterval:1
target:self
selector:@selector(updateDisplay:)
userInfo:nil
repeats:YES];
...
...
// Cancel method
[walkTimer invalidate];
walkTimer = nil;
// (4) Something not listed above ...
我很高兴只有(1)(2)(3)或(4),因为很多关于哪个最好的讨论已经写在Other线程上。似乎确实有很多相互矛盾的答案,所以我希望这个更具体的问题能够有助于关注这种情况下最佳实践。
作为Apple NSTimer Class Reference中的注释,示例代码项目中的5个中有4个使用为保留属性分配**的NSTimers。以下是类参考示例显示的示例:
@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];
**应该注意的是,在示例中,Apple直接分配iVar而不使用属性设置器。
答案 0 :(得分:18)
在给予我更多的思考并在我的推理中找到一个重要的缺陷之后,我得出了一个不同的结论:
无论您是否拥有需要无效的计时器的拥有或非拥有引用,都无关紧要。这完全是品味问题。
交易破坏者是,计时器的目标是什么:
如果创建计时器的对象是其目标,则管理该对象的生命周期变得更加脆弱:它不能简单地保留/释放托管,而是需要确保保存对该对象的最后一个引用的客户端使其无效处理它之前的计时器。
让我用几种对象图来说明情况:
如果你的对象只有曾经拥有一个单一实例,当它被妥善处理时,可以避免这种情况:
Proper disposal through cancellation: before disposing of yourObject through release, someClientObject calls the cancelTimer method on yourObject. Within that method, yourObject invalidates workTimer and (if it owned workTimer) disposes of workTimer through release http://a.yfrog.com/img614/7428/p8af.png
我知道没有简单的答案! (并非后者必须多说,但......)
所以我的建议是......
retain
设为计时器(因为这是避免的唯一方法)客户端导致的崩溃直接使他们访问的计时器失效。和提供您自己的setter。在我看来,重新安排一个计时器并不是打破封装的好理由:如果你需要这样做,就提供一个改变器。TimerTarget
类或 - 如果你可以使用它 - 通过MAZeroingWeakReference?)我为第一次讨论中的白痴而道歉,并要感谢Daniel Dickison和Rob Napier的耐心。
所以这是我从现在开始处理计时器的方式:
// 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;
// for 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 was %@)", 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
原始(完整披露):
您从your other post知道我的意见:
没有理由拥有预定计时器(和bbum seems to agree)的参考。
也就是说,您的选项 2 和 3 基本相同。 ([self setWalkTimer:nil]
超过walkTimer = nil
涉及其他消息传递,但我不确定编译器是否会对此进行优化并直接访问ivar,但是... ...
答案 1 :(得分:2)
我通常会管理访问者内部的无效状态,这样一旦你认为自己摆脱了访问权限,你就不会对访问你的计时器感到惊讶:
@property(nonatomic, retain) NSTimer *walkTimer;
[self setWalkTimer: ti];
- (void)setWalkTimer:(NSTimer *)aTimer
{
if (aTimer != walkTimer_)
{
[aTimer retain];
[walkTimer invalidate];
[walkTimer release];
walkTimer = aTimer;
}
}
...
...
// Cancel method
[self setWalkTimer:nil];
...
...
// Make a new timer, automatically invalidating the old one
[self setWalkTimer:[... a new timer ...]]
...
...
// dealloc method
[walkTimer_ invalidate];
[walkTimer_ release];
[super dealloc];