Обдумав все это и обнаружив важный недостаток в своих рассуждениях, я пришел к другому выводу:
Не имеет большого значения, являетесь ли вы владельцем или не владеетевладеть ссылкой на таймер, который вам нужно сделать недействительным.Это полностью дело вкуса.
Прерыватель сделки заключается в том, какова цель таймера:
Если объект, который создает таймер, является его целью, управление временем жизни этого объекта становится болеехрупкий: его нельзя просто сохранить / разблокировать, вместо этого вам нужно убедиться, что клиент, который хранит последнюю ссылку на этот объект, делает его недействительным таймером, прежде чем он его удалит.
Позвольте мне проиллюстрировать ситуацию спара графиков вида объектов:
Вы начинаете в состоянии, из которого вы устанавливаете таймер и ставите себя в качестве цели.Настройка таймера: yourObject
принадлежит someClientObject
.Параллельно существует текущий цикл выполнения с массивом scheduleTimers.метод setupTimer вызывается при yourObject
:
Результатом является следующее начальное состояние.В дополнение к прежнему состоянию yourObject
теперь имеет ссылку (принадлежит или нет) на workTimer
, которому, в свою очередь, принадлежит yourObject
.Кроме того, workTimer
принадлежит массиву runToops scheduleTimers:
Так что теперь вы будете использовать объект, но когда вы закончите с этим и просто освободите его, вы получите простую утечку: после того, как someClientObject
избавится от yourObject
через простой релиз, yourObject
отсоединится от объектного графа, но сохранитсяживы workTimer
.workTimer
и yourObject
просочились!
Где вы пропускаете объект (и таймер), потому что runloop поддерживает работу таймера, который - в свою очередь - сохраняет ссылку на владельцаваш объект.
Этого можно избежать, если yourObject
только когда-либо принадлежит одному экземпляру за один раз, когда он должным образом утилизируется надлежащимотмена: перед утилизацией yourObject
через релиз, someClientObject
вызывает метод cancelTimer
для вашего объекта.В рамках этого метода yourObject делает недействительным workTimer
и (если он владел workTimer
) удаляет workTimer через релиз:
Но теперь, как вы решаете следующую ситуацию?
Несколько владельцев: Настройка как вначальное состояние, но теперь с несколькими независимыми clientObjects
, которые содержат ссылки на yourObject
Нет простого ответа, я в курсе!(Не то, чтобы последний говорил много, но ...)
Так что мой совет ...
Не делайте свой таймер собственностью / донане предоставлю аксессуаров для него!Вместо этого, держите его в секрете (с современной средой выполнения, которую я думаю , вы можете зайти так далеко, чтобы определить ivar
в расширении класса) и работайте с ним только из одного объекта.(Вы можете сохранить его, если вам удобнее, но в этом нет абсолютно никакой необходимости.)
- Предупреждение: Если вам абсолютно необходимо чтобы получить доступ к таймеру из другого объекта, сделайте свойство
retain
таймером (так как это единственный способ избежать сбоев, вызванных клиентами, которые сделали недействительным таймер, к которому они обращались) и предоставляют свой собственный установщик.Перенастройка таймера, на мой взгляд, не очень хорошая причина нарушать инкапсуляцию: предоставьте мутатор, если вам нужно это сделать.
Установите таймер с цельюкроме себя.(Существует множество способов сделать это. Возможно, написав общий класс TimerTarget
или - если вы можете его использовать - через MAZeroingWeakReference
?)
Я прошу прощения за то, что был дебилом в первом обсуждении, и хочу поблагодарить Даниэля Dickison
и Роба Нейпира за их терпение.
Итак, вот как я собираюсь теперь обращаться с таймерами:
// 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
Оригинал (для полного раскрытия):
Вы знаете мое мнение от вашего другого поста :
Тамэто небольшая причина для использования ссылки на запланированный таймер (и bbum, похоже, согласен ).
Тем не менее, ваши параметры 2 и 3 по сути одинаковы.(В [self setWalkTimer:nil]
вместо walkTimer = nil
задействованы дополнительные сообщения, но я не уверен, что компилятор не оптимизирует это и не получит прямой доступ к ivar, но хорошо ...)