は[NSTimer scheduledTimerWithTimeInterval:...]
ターゲットを保持するため、ターゲットがselfの場合、モデルクラスのインスタンスの割り当てが解除されることはありません。
回避策として、別のオブジェクト(TimerTarget
次の例で呼び出されます)を使用できます。保持サイクルを回避するために、へTimerTarget
の弱参照があります。ModelClass
この「ヘルパークラス」はこんな感じ。その唯一の目的は、タイマーイベントを「実際のターゲット」に転送することです。
@interface TimerTarget : NSObject
@property(weak, nonatomic) id realTarget;
@end
@implementation TimerTarget
- (void)timerFired:(NSTimer*)theTimer
{
[self.realTarget performSelector:@selector(timerFired:) withObject:theTimer];
}
@end
これで、モデルクラスで、タイマーを作成して無効にすることができますdealloc
。
@interface ModelClass ()
@property(strong, nonatomic) NSTimer *timer;
@end
@implementation ModelClass
- (id)init
{
self = [super init];
if (self) {
TimerTarget *timerTarget = [[TimerTarget alloc] init];
timerTarget.realTarget = self;
self.timer = [NSTimer scheduledTimerWithTimeInterval:1
target:timerTarget
selector:@selector(timerFired:)
userInfo:nil repeats:YES];
}
return self;
}
- (void)dealloc
{
[self.timer invalidate]; // This releases the TimerTarget as well!
NSLog(@"ModelClass dealloc");
}
- (void)timerFired:(NSTimer*)theTimer
{
NSLog(@"Timer fired");
}
@end
だから私たちは
modelInstance ===> timer ===> timerTarget ---> modelInstance
(===> : strong reference, ---> : weak reference)
タイマーからモデルクラスのインスタンスへの(強力な)参照はもうないことに注意してください。
次のコードでこれをテストしました。このコードは、のインスタンスを作成し、ModelClass
5秒後にリリースします。
__block ModelClass *modelInstance = [[ModelClass alloc] init];
int64_t delayInSeconds = 5.0;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
modelInstance = nil;
});
出力:
2013-01-23 23:54:11.483 timertest[16576:c07] Timer fired
2013-01-23 23:54:12.483 timertest[16576:c07] Timer fired
2013-01-23 23:54:13.483 timertest[16576:c07] Timer fired
2013-01-23 23:54:14.483 timertest[16576:c07] Timer fired
2013-01-23 23:54:15.483 timertest[16576:c07] Timer fired
2013-01-23 23:54:15.484 timertest[16576:c07] ModelClass dealloc