重复NSTimer,弱引用,拥有引用或iVar? [英] Repeating NSTimer, weak reference, owning reference or iVar?

查看:77
本文介绍了重复NSTimer,弱引用,拥有引用或iVar?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想我会把这个在这里作为一个单独的问题从我以前的
?)



这是我从现在开始处理计时器的方式:

  // NSTimer + D12WeakTimerTarget.h:
#import< Foundation / NSTimer.h>
@interface NSTimer(D12WeakTimerTarget)
+(NSTimer *)D12scheduledTimerWithTimeInterval:(NSTimeInterval)ti weakTarget:(id)target selector:(SEL)selector userInfo:(id)userInfo repeates:(BOOL)shouldRepeat logsDeallocation :(BOOL)shouldLogDealloc;
@end

// NSTimer + D12WeakTimerTarget.m:
#importNSTimer + D12WeakTimerTarget.h
@interface D12WeakTimerTarget:NSObject {
__weak id weakTarget;
SEL selector;
//用于日志记录:
BOOL日志记录;
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]! @),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: )selector userInfo:(id)userInfo repeates:(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];

返回Timer;
}
@end






原始(完整披露):



您知道我的意见来自您的其他帖子



的预定定时器(并且 bbum似乎同意)。 p>

也就是说,你的选项 2 3 本质上是一样的。 (在 [self setWalkTimer:nil] over walkTimer = nil 中还有其他消息,但我不确定编译器不会优化它,直接访问ivar,但很好...)


I thought I would put this out here as a separate question from my previous retaining-repeating-nstimer-for-later-access as the discussion has moved forward making a new question clearer than yet another EDIT:

The scenario is an object creates a repeating NSTimer, lets say in viewDidLoad, once created the NSTimer needs to stay around so it can be accessed by other methods.

NSTimer *ti = [NSTimer scheduledTimerWithTimeInterval:1 
                                               target:self 
                                             selector:@selector(updateDisplay:) 
                                             userInfo:nil 
                                              repeats:YES];

I understand that when created the runloop takes ownership of the NSTimer and ultimately stops, removes and releases the NSTimer when [ti invalidate]; is called.

By virtue of the fact that we need to access the NSTimer in more than one method we need some way to hold a reference for future use, the revised question is:

// (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 ...

I am happy for just (1) (2) (3) or (4) as a lot of discussion regarding which is best has already been written on the Other thread. There does seem to be a lot of conflicting answers so I hope this more specific question will help focus on what might be best practice in this situation.


EDIT:

As a side note in the Apple NSTimer Class Reference 4 out of 5 of the sample code projects use NSTimers that are assigned** to a retained property. Here is an example of what the 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];

** It should be noted that in the examples Apple are assigning the iVar directly and not using the property setter.

解决方案

Edit

After giving it all some more thought and finding an important flaw in my reasoning, I've come to a different conclusion:

It doesn't matter much, whether you hold an owning or a non-owning reference to a timer that you need to invalidate. It is completely a matter of taste.

The deal breaker is, what the target of the timer is:

If the object that creates a timer is its target, managing that object's lifetime becomes more fragile: it cannot simply be retain/release managed, instead you need to ensure that the client that holds the last reference to this object makes it invalidate the timer before it disposes of it.

Let me illustrate the situation with a couple of sort-of-object-graphs:

  1. You start in a state from which you setup the timer and set yourself as the target:
  2. The result is the following initial state:
  3. So now you'll use the object, but when you're done with it and simply release it, you'll end up with

    where you leak the object (and the timer) because the runloop keeps the timer alive, which — in turn — keeps an owning reference to your object.

This can be avoided if yourObject is only ever owned by one single instance at a time, when it is properly disposed of:

But now, how do you resolve the following situation?

There is no easy answer, I am aware of! (Not that the latter has to say much, but...)

So my advice is...

  1. Don't make your timer a property/don't provide accessors for it! Instead, keep it private (with the modern runtime i think you could go so far as to define the ivar in a class extension) and only deal with it from one single object. (You may retain it, if you feel more comfortable doing so, but there is absolutely no need for it.)
    • Caveat: If you absolutely need to access the timer from another object, make the property retain the timer (as that is the only way to avoid crashes caused by clients that directly invalidated the timer they accessed) and provide your own setter. Rescheduling a timer is — in my opinion — not a good reason to break encapsulation here: provide a mutator if you need to do that.
  2. Set the timer up with a target other than self. (There are plenty of ways doing so. Maybe through writing a generic TimerTarget class or — if you can use it — through an MAZeroingWeakReference?)

I apologize for being a moron in the first discussion and want to thank Daniel Dickison and Rob Napier for their patience.

So here is the way I am going to handle timers from now 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;
    // 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


Original (for full disclosure):

You know my opinion from your other post:

There is little reason for an owning reference of a scheduled timer (and bbum seems to agree).

That said, your options 2 and 3 are essentially the same. (There is additional messaging involved in [self setWalkTimer:nil] over walkTimer = nil but I'm not sure if the compiler won't optimize that away and access the ivar directly, but well...)

这篇关于重复NSTimer,弱引用,拥有引用或iVar?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆