在两行代码执行之间添加延迟

88

我需要在同一个函数中的两行代码执行之间添加延迟。有没有合适的选项可以实现这个功能?

注意:我不需要使用两个不同的函数来实现这个功能,而且延迟不能影响其他函数的执行。

例如:

line 1: [executing first operation];

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

line 3: [executing second operation];
7个回答

238

你可以使用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)
}

在添加延迟之前,您仍应该问自己“我真的需要添加延迟吗”,因为它经常会使代码复杂化并引起竞态条件。


2
警告人们是正确的,但如果您编写需要等待具有固定处理时间的外部设备的控制器,则这并不是无意义的。 - user2161301
3
在搜索框中输入内容时延迟HTTP请求怎么样?边输入边过滤结果呢?在用户输入每个字符时等待一秒钟然后请求筛选后的列表,而不是为每个按键都向服务器进行查询,这些听起来对我来说并不是无意义的。 - Alejandro Iván

29

您可以使用NSThread方法:

[NSThread sleepForTimeInterval: delay];

但是,如果您在主线程上执行此操作,将会阻塞应用程序,因此请仅在后台线程上执行此操作。


或者使用Swift语言:

NSThread.sleepForTimeInterval(delay)

在 Swift 3 中

Thread.sleep(forTimeInterval: delay)

2
根据问题的要求,我相信这应该是被接受的答案。 - aclima
1
@ACLima:抱歉,'sleepForTimeInterval'会使线程在给定的时间间隔内休眠,这可能会影响其他同时执行的函数。 - Krishna Raj Salim
它是第二还是分钟? - Shiva

25

这行代码会在3秒钟后调用选择器secondMethod:

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

在您进行第二个操作时使用它,并设置所需的延迟。如果您有很多代码,请将其放入自己的方法中,并使用performSelector:调用该方法。这不会像sleep那样阻塞UI。

编辑:如果您不想要第二个方法,可以添加一个类别以便能够使用带有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);
}

6
原文:The OP explicitly states they do not want a second method :S翻译:原帖明确表示他们不想要第二种方法。:S - Paul.s
他可以添加一个类别,以便能够使用块来替代performSelector:https://dev59.com/XG865IYBdhLWcg3wEKSZ - Sunkas
@Sunkas:感谢您的回答。但是我已经提到我不想添加第二个函数了。 - Krishna Raj Salim
3
使用这个答案将会给你一个非常简洁的方法来延迟执行代码,而不需要使用第二个方法。 [self performBlock:^{ your_code } afterDelay:0.1]; - Sunkas
4
如果 OP 不愿意添加另一种方法,他们应该解释为什么,因为这是一种非常好的使用工具的方式。方法不好吗?他们是否正在使用无效支付的方法? - tooluser

8

我有几个回合制游戏,需要AI在进行其回合之前(以及在其回合的步骤之间)暂停。我相信还有其他更有用的情况下,延迟是最好的解决方案。在Swift中:

        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的调用是否不同。(我也需要把这个添加到那个上面。)


7

[已于2020年11月27日进行验证并确认与Xcode 12.1兼容]

如今最方便的方法:Xcode提供了一个代码片段,您只需输入延迟值和您希望在延迟之后运行的代码即可。

  1. 点击Xcode右上角的+按钮。
  2. 搜索after
  3. 它只会返回一个搜索结果,这就是所需的代码片段(见屏幕截图)。双击它就可以使用了。

screenshot illustrating how to get the snippet from within Xcode itself


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];
});

1
你是指iOS 4吗?Grand central dispatch 函数参考 - Paul.s

2

就像@Sunkas所写的那样,performSelector:withObject:afterDelay:dispatch_after的对应物,只是它更短,并且使用常规的objective-c语法。如果您需要将参数传递给要延迟的块,您可以通过withObject参数传递它们,然后在调用selector时接收它们:

[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:"方法。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接