Разработчик iPhone — PerformSelector:withObject:afterDelay или NSTimer?
-
18-09-2019 - |
Вопрос
Чтобы повторить вызов метода (или отправку сообщения, я думаю, это подходящий термин) каждые Икс секунд, лучше ли использовать NSTimer (scheduledTimerWithTimeInterval:target:selector:userInfo:repeats:) или рекурсивно вызывать метод в конце (используя PerformSelector:withObject:afterDelay)?Последний не использует объект, но, возможно, он менее понятен/читабельен?Кроме того, просто чтобы вы имели представление о том, что я делаю, это просто представление с меткой, которая ведет обратный отсчет до 12:00, а когда оно достигает 0, время мигает (00:00:00). и воспроизводить звуковой сигнал вечно.
Спасибо.
Редактировать:Кроме того, как лучше всего повторно воспроизводить SystemSoundID (навсегда)?Редактировать:В итоге я использовал это для постоянного воспроизведения SystemSoundID:
// Utilities.h
#import <Foundation/Foundation.h>
#import <AudioToolbox/AudioServices.h>
static void soundCompleted(SystemSoundID soundID, void *myself);
@interface Utilities : NSObject {
}
+ (SystemSoundID)createSystemSoundIDFromFile:(NSString *)fileName ofType:(NSString *)type;
+ (void)playAndRepeatSystemSoundID:(SystemSoundID)soundID;
+ (void)stopPlayingAndDisposeSystemSoundID;
@end
// Utilities.m
#import "Utilities.h"
static BOOL play;
static void soundCompleted(SystemSoundID soundID, void *interval) {
if(play) {
[NSThread sleepForTimeInterval:(NSTimeInterval)interval];
AudioServicesPlaySystemSound(soundID);
} else {
AudioServicesRemoveSystemSoundCompletion(soundID);
AudioServicesDisposeSystemSoundID(soundID);
}
}
@implementation Utilities
+ (SystemSoundID)createSystemSoundIDFromFile:(NSString *)fileName ofType:(NSString *)type {
NSString *path = [[NSBundle mainBundle] pathForResource:fileName ofType:type];
SystemSoundID soundID;
NSURL *filePath = [NSURL fileURLWithPath:path isDirectory:NO];
AudioServicesCreateSystemSoundID((CFURLRef)filePath, &soundID);
return soundID;
}
+ (void)playAndRepeatSystemSoundID:(SystemSoundID)soundID interval:(NSTimeInterval)interval {
play = YES
AudioServicesAddSystemSoundCompletion(soundID, NULL, NULL,
soundCompleted, (void *)interval);
AudioServicesPlaySystemSound(soundID);
}
+ (void)stopPlayingAndDisposeSystemSoundID {
play = NO
}
@end
Вроде работает нормально..А для мигания метки, я думаю, я буду использовать NSTimer.
Решение
Таймер больше подходит на строго определенный интервал.Вы потеряете точность, если ваша функция будет вызывать себя с задержкой, потому что на самом деле это не так. синхронизировано к временному интервалу.Всегда есть время, необходимое для запуска самого метода, который выделяет интервал.
Я бы сказал, придерживайтесь NSTimer.
Другие советы
Чтобы добавить немного к другим ответам, случай рекурсивного вызова будет тогда, когда вызов может занять неизвестное количество времени - скажем, вы неоднократно вызываете веб-службу с небольшими объемами данных, пока не закончите.Каждый вызов может занять некоторое неизвестное количество времени, поэтому код ничего не делает, пока не вернется веб-вызов, затем отправляется следующий пакет до тех пор, пока не перестанут отправляться данные и код не вызовет себя снова.
Поскольку ваше приложение зависит от точности времени (т.он должен выполняться один раз в секунду), лучше использовать NSTimer.Для выполнения самого метода требуется некоторое время, и NSTimer с этим справится (если ваш метод занимает менее 1 секунды, если он вызывается каждую секунду).
Чтобы повторно воспроизводить звук, вы можете установить обратный вызов завершения и воспроизвести звук там:
SystemSoundID tickingSound;
...
AudioServicesAddSystemSoundCompletion(tickingSound, NULL, NULL, completionCallback, (void*) self);
...
static void completionCallback(SystemSoundID mySSID, void* myself) {
NSLog(@"completionCallback");
// You can use this when/if you want to remove the completion callback
//AudioServicesRemoveSystemSoundCompletion(mySSID);
// myself is the object that called set the callback, because we set it up that way above
// Cast it to whatever object that is (e.g. MyViewController, in this case)
[(MyViewController *)myself playSound:mySSID];
}