如何在延迟后触发一个块,比如-performSelector:withObject:afterDelay:?

Egi*_*gil 725 objective-c grand-central-dispatch ios objective-c-blocks

有没有办法在延迟后调用带有原始参数的块,比如使用performSelector:withObject:afterDelay:但是使用像int/ double/ 这样的参数float

Rya*_*yan 1167

我想你在找dispatch_after().它要求您的块不接受任何参数,但您可以让块从本地范围捕获这些变量.

int parameter1 = 12;
float parameter2 = 144.1;

// Delay execution of my block for 10 seconds.
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 10 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
    NSLog(@"parameter1: %d parameter2: %f", parameter1, parameter2);
});
Run Code Online (Sandbox Code Playgroud)

更多:https://developer.apple.com/documentation/dispatch/1452876-dispatch_after

  • 实际上,那不是真的.未被标记为__block存储的块捕获的对象由块保留,并在块被销毁时(当其保留计数变为0时)被块释放.这是关于它的文档:http://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/Blocks/Articles/bxVariables.html#//apple_ref/doc/uid/TP40007502-CH6-SW3 (88认同)
  • `dispatch_get_current_queue()`现在已弃用 (20认同)
  • 这个`dispatch_time(DISPATCH_TIME_NOW,10ull*NSEC_PER_SEC)`片段很讨厌.对此没有更清洁的方法吗? (9认同)
  • 除NSEC_PER_SEC外,NSEC_PER_MSEC也存在,如果你想指定毫秒;) (9认同)
  • 是的,`dispatch_get_current_queue()`总是返回运行代码的队列.因此,当从主线程运行此代码时,该块也将在主线程上执行. (7认同)
  • 非常酷,似乎(在ARC下)`dispatch_after`保留块直到它执行它.这消除了如此复杂的代码...... (4认同)
  • 如果希望它在主队列上运行,可以使用`dispatch_get_main_queue()`而不是当前队列. (2认同)

Ste*_*ing 493

您可以dispatch_after稍后用来调用块.在Xcode中,开始输入dispatch_after并点击Enter自动完成以下内容:

在此输入图像描述

这是一个以两个浮点数作为"参数"的示例.您不必依赖任何类型的宏,并且代码的意图非常明确:

Swift 3,Swift 4

let time1 = 8.23
let time2 = 3.42

// Delay 2 seconds
DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) {
    print("Sum of times: \(time1 + time2)")
}
Run Code Online (Sandbox Code Playgroud)

斯威夫特2

let time1 = 8.23
let time2 = 3.42

// Delay 2 seconds
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(2.0 * Double(NSEC_PER_SEC))), dispatch_get_main_queue()) { () -> Void in
        println("Sum of times: \(time1 + time2)")
}
Run Code Online (Sandbox Code Playgroud)

目标C.

CGFloat time1 = 3.49;
CGFloat time2 = 8.13;

// Delay 2 seconds
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
    CGFloat newTime = time1 + time2;
    NSLog(@"New time: %f", newTime);
});
Run Code Online (Sandbox Code Playgroud)

  • 小心延迟时间不是两倍.所以只是不要尝试NSEC_PER_SEC*0.5半秒钟它将无法工作!您需要降低毫秒并使用NSEC_PER_MSEC*500.因此您应该将代码示例更改为:int delayInSeconds = 2以显示人们不能使用NSEC_PER_SEC的分数. (43认同)
  • @malhal实际上,`NSEC_PER_SEC*0.5`的工作方式与`NSEC_PER_MSEC*500`相同.虽然你正确地指出`dispatch_time`需要一个64位整数,但它所期望的值是以纳秒为单位.`NSEC_PER_SEC`定义为`1000000000ull`,并将其乘以浮点常量`0.5`将隐式执行浮点运算,产生`500000000.0`,然后将其显式转换回64位整数.所以使用一小部分`NSEC_PER_SEC`是完全可以接受的. (9认同)

War*_*shi 197

如何使用Xcode内置代码片段库?

在此输入图像描述

Swift更新:

很多投票激励我更新这个答案.

内置的Xcode代码段库dispatch_after仅用于objective-c语言.人们还可以创建自己的自定义代码段Swift.

在Xcode中写下这个.

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(<#delayInSeconds#> * Double(NSEC_PER_SEC))), dispatch_get_main_queue(), {
        <#code to be executed after a specified delay#>
    })
Run Code Online (Sandbox Code Playgroud)

拖动此代码并将其放在代码段库区域中. 在此输入图像描述

在代码片段列表的底部,会有一个名为的新实体My Code Snippet.编辑此标题.当您输入Xcode时输入建议Completion Shortcut.

有关详细信息,请参阅CreatingaCustomCodeSnippet.

更新Swift 3

拖动此代码并将其放在代码段库区域中.

DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(<#delayInSeconds#>)) {
    <#code to be executed after a specified delay#>
}
Run Code Online (Sandbox Code Playgroud)

  • 有没有人在Xcode中实际使用此功能?我更喜欢将其键入为代码建议弹出窗口,并且使用起来同样简单. (19认同)
  • 直到知道我只是认为复制和粘贴是最简单的编码方式.现在我只是拖放......哈哈哈 (6认同)

Oli*_*ain 57

扩展了Jaime Cham的答案,我创建了一个NSObject + Blocks类别,如下所示.我觉得这些方法更符合现有的performSelector:NSObject方法

NSObject的+ Blocks.h

#import <Foundation/Foundation.h>

@interface NSObject (Blocks)

- (void)performBlock:(void (^)())block afterDelay:(NSTimeInterval)delay;

@end
Run Code Online (Sandbox Code Playgroud)

NSObject的+ Blocks.m

#import "NSObject+Blocks.h"

@implementation NSObject (Blocks)

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

- (void)performBlock:(void (^)())block afterDelay:(NSTimeInterval)delay
{
    void (^block_)() = [block copy]; // autorelease this if you're not using ARC
    [self performSelector:@selector(performBlock:) withObject:block_ afterDelay:delay];
}

@end
Run Code Online (Sandbox Code Playgroud)

并使用如下:

[anyObject performBlock:^{
    [anotherObject doYourThings:stuff];
} afterDelay:0.15];
Run Code Online (Sandbox Code Playgroud)

  • `delay`应该是`NSTimeInterval`(这是一个`double`).`#import <UIKit/UIKit.h>`不需要.并且,我不明白为什么` - (void)performBlock:(void(^)())block;`可能有用,因此可以从头中删除. (5认同)

Jai*_*ham 21

也许比通过GCD,在某个类(例如"Util")或者对象上的类别更简单:

+ (void)runBlock:(void (^)())block
{
    block();
}
+ (void)runAfterDelay:(CGFloat)delay block:(void (^)())block 
{
    void (^block_)() = [[block copy] autorelease];
    [self performSelector:@selector(runBlock:) withObject:block_ afterDelay:delay];
}
Run Code Online (Sandbox Code Playgroud)

所以使用:

[Util runAfterDelay:2 block:^{
    NSLog(@"two seconds later!");
}];
Run Code Online (Sandbox Code Playgroud)

  • @Jaimie Cham为什么你认为通过GCD很难? (3认同)
  • 通过GCD确实与PerformSelector:afterDelay:略有不同的行为,因此可能有理由不使用GCD.例如,请参阅以下问题:http://stackoverflow.com/questions/10440412/perform-on-next-run-loop-whats-wrong-with-gcd (2认同)

Ant*_*ine 21

对于Swift,我使用该dispatch_after方法创建了一个全局函数,没什么特别的.我更喜欢这个,因为它易读且易于使用:

func performBlock(block:() -> Void, afterDelay delay:NSTimeInterval){
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(delay * Double(NSEC_PER_SEC))), dispatch_get_main_queue(), block)
}
Run Code Online (Sandbox Code Playgroud)

您可以使用以下内容:

performBlock({ () -> Void in
    // Perform actions
}, afterDelay: 0.3)
Run Code Online (Sandbox Code Playgroud)


Dan*_*ark 16

这是我的2美分= 5种方法;)

我喜欢封装这些细节,并让AppCode告诉我如何完成我的句子.

void dispatch_after_delay(float delayInSeconds, dispatch_queue_t queue, dispatch_block_t block) {
    dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
    dispatch_after(popTime, queue, block);
}

void dispatch_after_delay_on_main_queue(float delayInSeconds, dispatch_block_t block) {
    dispatch_queue_t queue = dispatch_get_main_queue();
    dispatch_after_delay(delayInSeconds, queue, block);
}

void dispatch_async_on_high_priority_queue(dispatch_block_t block) {
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), block);
}

void dispatch_async_on_background_queue(dispatch_block_t block) {
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), block);
}

void dispatch_async_on_main_queue(dispatch_block_t block) {
    dispatch_async(dispatch_get_main_queue(), block);
}
Run Code Online (Sandbox Code Playgroud)


Aug*_*ine 8

PerformSelector:WithObject总是接受一个对象,所以为了传递像int/double/float等参数.....你可以使用这样的东西.

// NSNumber是一个对象..

[self performSelector:@selector(setUserAlphaNumber:)
     withObject: [NSNumber numberWithFloat: 1.0f]       
     afterDelay:1.5];



-(void) setUserAlphaNumber: (NSNumber*) number{

     [txtUsername setAlpha: [number floatValue] ];

}
Run Code Online (Sandbox Code Playgroud)

同样的方法你可以使用[NSNumber numberWithInt:]等....在接收方法中你可以将数字转换为格式为[number int]或[number double].


Him*_*jan 8

dispatch_after函数在给定的时间段之后将块对象调度到调度队列.使用以下代码在2.0秒后执行一些与UI相关的操作.

            let delay = 2.0
            let delayInNanoSeconds = dispatch_time(DISPATCH_TIME_NOW, Int64(delay * Double(NSEC_PER_SEC)))
            let mainQueue = dispatch_get_main_queue()

            dispatch_after(delayInNanoSeconds, mainQueue, {

                print("Some UI related task after delay")
            })
Run Code Online (Sandbox Code Playgroud)

在swift 3.0中:

            let dispatchTime: DispatchTime = DispatchTime.now() + Double(Int64(2.0 * Double(NSEC_PER_SEC))) / Double(NSEC_PER_SEC)
            DispatchQueue.main.asyncAfter(deadline: dispatchTime, execute: {

          })
Run Code Online (Sandbox Code Playgroud)


mid*_*n p 7

Xcode 10.2 和 Swift 5 及以上

DispatchQueue.main.asyncAfter(deadline: .now() + 2, execute: {
   // code to execute                 
})
Run Code Online (Sandbox Code Playgroud)


osk*_*rko 6

按 Cmd + Shift + L 显示 Xcode 内置代码片段库:

在此输入图像描述

之后查找调度,然后只需拖放到您的代码中即可。


Jee*_*hut 5

这是一个方便的助手,可以防止反复拨打恼人的GCD电话:

public func delay(bySeconds seconds: Double, dispatchLevel: DispatchLevel = .main, closure: @escaping () -> Void) {
    let dispatchTime = DispatchTime.now() + seconds
    dispatchLevel.dispatchQueue.asyncAfter(deadline: dispatchTime, execute: closure)
}

public enum DispatchLevel {
    case main, userInteractive, userInitiated, utility, background
    var dispatchQueue: DispatchQueue {
        switch self {
        case .main:                 return DispatchQueue.main
        case .userInteractive:      return DispatchQueue.global(qos: .userInteractive)
        case .userInitiated:        return DispatchQueue.global(qos: .userInitiated)
        case .utility:              return DispatchQueue.global(qos: .utility)
        case .background:           return DispatchQueue.global(qos: .background)
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

现在,您只需将代码延迟到主线程上,如下所示:

delay(bySeconds: 1.5) { 
    // delayed code
}
Run Code Online (Sandbox Code Playgroud)

如果您想将代码延迟到不同的线程:

delay(bySeconds: 1.5, dispatchLevel: .background) { 
    // delayed code that will run on background thread
}
Run Code Online (Sandbox Code Playgroud)

如果你更喜欢一个也有一些更方便功能的框架,那么请查看HandySwift.您可以通过Carthage将其添加到项目中,然后使用它与上面的示例完全相同:

import HandySwift    

delay(bySeconds: 1.5) { 
    // delayed code
}
Run Code Online (Sandbox Code Playgroud)


cpi*_*off 5

这是Swift 3在延迟后排队工作的方法.

DispatchQueue.main.asyncAfter(
  DispatchTime.now() + DispatchTimeInterval.seconds(2)) {
    // do work
}
Run Code Online (Sandbox Code Playgroud)


Gen*_*ari 1

您可以将参数包装在您自己的类中,也可以将方法调用包装在不需要在基本类型中传递的方法中。然后在延迟后调用该方法,并在该方法中执行您想要执行的选择器。