iPhone 开发——performSelector:withObject:afterDelay 还是 NSTimer?

发布于 2024-08-06 15:56:46 字数 1999 浏览 7 评论 0原文

要每 x 秒重复一次方法调用(或消息发送,我猜合适的术语是),最好使用 NSTimer (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。

To repeat a method call (or message send, I guess the appropriate term is) every x seconds, is it better to use an NSTimer (NSTimer's scheduledTimerWithTimeInterval:target:selector:userInfo:repeats:) or to have the method recursively call itself at the end (using performSelector:withObject:afterDelay)? The latter doesn't use an object, but maybe its less clear/readable? Also, just to give you an idea of what I'm doing, its just a view with a label which counts down to 12:00 midnight, and when it gets to 0, it will blink the time (00:00:00) and play a beep sound forever.

Thanks.

Edit: also, what would be the best way to repeatedly play a SystemSoundID (forever) ?
Edit: I ended up using this to play the SystemSoundID forever:

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

Seems to work fine.. And for the label blinking I'll use an NSTimer I guess.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

我一向站在原地 2024-08-13 15:56:46

计时器更适合严格定义的间隔。如果您的函数调用本身有延迟,您将失去准确性,因为它没有真正同步到时间间隔。运行实际方法本身总是需要时间,这会导致间隔消失。

我想说,坚持使用 NSTimer。

A timer is more suited to a strictly defined interval. You will lose accuracy if you have your function call itself with a delay because its not really synced to a time interval. There's always the time taken to run the actual method itself as well which puts the interval out.

Stick with an NSTimer, I'd say.

同展鸳鸯锦 2024-08-13 15:56:46

只是为了向其他答案添加一点,递归调用的情况是调用可能需要未知的时间 - 假设您正在使用少量数据重复调用 Web 服务,直到完成。每个调用可能会花费一些未知的时间,因此您可以让代码在网络调用返回之前不执行任何操作,然后发送下一批,直到没有更多数据需要发送并且代码不会再次调用自身。

Just to add a bit to the other answers, the case for a recursive call would be when the call might take an unknown amount of time - say you are calling a web service repeatedly with small amounts of data until you are finished. Each call may take some unknown amount of time so you have the code do nothing until the web call returns, then the next batch is sent out until no more data remains to be sent and the code does not call itself again.

甚是思念 2024-08-13 15:56:46

由于您的应用程序取决于时间精度(即它需要每秒执行一次),因此 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];
}

Since your application depends on time accuracy (i.e. it needs to execute once per second), the NSTimer would be better. It takes some time for the method itself to execute, and an NSTimer would be fine with that (as long as your method takes less than 1 second, if it's called every second).

To repeatedly play your sound, you can set a completion callback and replay the sound there:

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];
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文