83

我需要在(相同)函数中的两行执行之间添加延迟。有什么有利的选择吗?

注意:我不需要两个不同的函数来做到这一点,延迟一定不能影响其他函数的执行。

例如:

line 1: [executing first operation];

line 2: Delay                        /* I need to introduce delay here */

line 3: [executing second operation];

任何帮助都是可观的。提前致谢...

4

7 回答 7

224

您可以使用 gcd 来执行此操作,而无需创建其他方法

// ObjC

NSTimeInterval delayInSeconds = 2.0;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
  NSLog(@"Do some work");
});

// Swift

DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
    print("Do some work)
}

您仍然应该问自己“我真的需要添加延迟吗”,因为它通常会使代码复杂化并导致竞争条件

于 2013-03-11T10:12:33.090 回答
27

您可以使用以下NSThread方法:

[NSThread sleepForTimeInterval: delay];

但是,如果您在主线程上执行此操作,您将阻止应用程序,因此只能在后台线程上执行此操作。


或在斯威夫特

NSThread.sleepForTimeInterval(delay)

在斯威夫特 3

Thread.sleep(forTimeInterval: delay)
于 2014-07-29T12:50:12.970 回答
22

此行在 3 秒后调用选择器 secondMethod:

[self performSelector:@selector(secondMethod) withObject:nil afterDelay:3.0 ];

在您的第二次操作中使用它并获得所需的延迟。如果您有很多代码,请将其放在自己的方法中并使用performSelector:. 它不会像sleep

编辑:如果您不想要第二种方法,您可以添加一个类别以便能够将块与 performSelector 一起使用:

@implementation NSObject (PerformBlockAfterDelay)

- (void)performBlock:(void (^)(void))block 
          afterDelay:(NSTimeInterval)delay
{
    block = [block copy];
    [self performSelector:@selector(fireBlockAfterDelay:) 
               withObject:block 
               afterDelay:delay];
}

- (void)fireBlockAfterDelay:(void (^)(void))block
{
    block();
}

@end

或者甚至更清洁:

void RunBlockAfterDelay(NSTimeInterval delay, void (^block)(void))
{
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC*delay),
      dispatch_get_current_queue(), block);
}
于 2013-03-11T10:02:33.983 回答
8

我有几个回合制游戏,我需要 AI 在轮到它之前暂停(以及在其轮到的步骤之间)。我确信还有其他更有用的情况,延迟是最好的解决方案。在斯威夫特:

        let delay = 2.0 * Double(NSEC_PER_SEC) 
        let time = dispatch_time(DISPATCH_TIME_NOW, Int64(delay)) 
        dispatch_after(time, dispatch_get_main_queue()) { self.playerTapped(aiPlayView) }

我刚回到这里,看看 Objective-C 调用是否不同。(我也需要将这个添加到那个中。)

于 2014-08-13T23:34:38.473 回答
5

[检查于 2020 年 11 月 27 日,并确认使用 Xcode 12.1 仍然准确]

目前最方便的方法:Xcode 提供了一个代码片段来执行此操作,您只需输入延迟值和您希望在延迟后运行的代码。

  1. 单击+Xcode 右上角的按钮。
  2. 搜索after
  3. 它将仅返回 1 个搜索结果,即所需的片段(见屏幕截图)。双击它,你就可以开始了。

说明如何从 Xcode 本身中获取代码段的屏幕截图

于 2020-03-01T03:44:34.917 回答
4

如果您的目标是 iOS 4.0+,您可以执行以下操作:

[executing first operation];
double delayInSeconds = 2.0;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
    [executing second operation];
});
于 2013-03-11T10:16:31.883 回答
2

就像@Sunkas 所写的那样,只是它更短并且您具有正常语法performSelector:withObject:afterDelay:的吊坠。如果你需要将参数传递给你想要延迟的块,你可以通过参数传递它们,你会在你的调用中收到它:dispatch_afterobjective-cwithObjectselector

[self performSelector:@selector(testStringMethod:) 
           withObject:@"Test Test" 
           afterDelay:0.5];

- (void)testStringMethod:(NSString *)string{
    NSLog(@"string  >>> %@", string);
}

如果您仍然想选择自己是在主线程还是在当前线程上执行它,那么有一些特定的方法可以让您指定这一点。苹果文档告诉我们:

如果您希望在运行循环处于默认模式以外的模式时使消息出队,请改用 performSelector:withObject:afterDelay:inModes: 方法。如果不确定当前线程是否是主线程,可以使用 performSelectorOnMainThread:withObject:waitUntilDone: 或 performSelectorOnMainThread:withObject:waitUntilDone:modes: 方法来保证选择器在主线程上执行。要取消排队的消息,请使用 cancelPreviousPerformRequestsWithTarget: 或 cancelPreviousPerformRequestsWithTarget:selector:object: 方法。

于 2015-01-20T10:37:47.927 回答