首页 文章

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

提问于
浏览
704

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

17 回答

  • 1132

    我相信作者不会问如何等待一个小数时间(延迟),而是如何传递一个标量作为选择器的参数(withObject :)和现代目标C中最快的方法是:

    [obj performSelector:...  withObject:@(0.123123123) afterDelay:10]
    

    您的选择器必须将其参数更改为NSNumber,并使用floatValue或doubleValue等选择器检索该值

  • 188

    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")
                })
    

    在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: {
    
              })
    
  • 8

    对于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)
    }
    

    您可以使用以下内容:

    performBlock({ () -> Void in
        // Perform actions
    }, afterDelay: 0.3)
    
  • 4

    这是我的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);
    }
    
  • 21

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

    enter image description here

    Update for Swift:

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

    内置的Xcode代码段库仅 objective-c 语言为 dispatch_after . 人们也可以为 Swift 创建自己的 Custom Code Snippet .

    在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#>
        })
    

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

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

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

    更新Swift 3

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

    DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(<#delayInSeconds#>)) {
        <#code to be executed after a specified delay#>
    }
    
  • 4

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

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

  • 8

    BlocksKit框架中有一个很好的 .

    BlocksKit

    (和 class )

    BBlocksKit.m

  • 21

    Swift 3 & Xcode 8.3.2

    这段代码对你有所帮助,我也加了解释

    // Create custom class, this will make your life easier
    class CustomDelay {
    
        static let cd = CustomDelay()
    
        // This is your custom delay function
        func runAfterDelay(_ delay:Double, closure:@escaping ()->()) {
            let when = DispatchTime.now() + delay
            DispatchQueue.main.asyncAfter(deadline: when, execute: closure)
        }
    }
    
    
    // here how to use it (Example 1)
    class YourViewController: UIViewController {
    
        // example delay time 2 second
        let delayTime = 2.0
    
        override func viewDidLoad() {
            super.viewDidLoad()
    
            CustomDelay.cd.runAfterDelay(delayTime) {
                // This func will run after 2 second
                // Update your UI here, u don't need to worry to bring this to the main thread because your CustomDelay already make this to main thread automatically :)
                self.runFunc()
            }
        }
    
        // example function 1
        func runFunc() {
            // do your method 1 here
        }
    }
    
    // here how to use it (Example 2)
    class YourSecondViewController: UIViewController {
    
        // let say you want to user run function shoot after 3 second they tap a button
    
        // Create a button (This is programatically, you can create with storyboard too)
        let shootButton: UIButton = {
            let button = UIButton(type: .system)
            button.frame = CGRect(x: 15, y: 15, width: 40, height: 40) // Customize where do you want to put your button inside your ui
            button.setTitle("Shoot", for: .normal)
            button.translatesAutoresizingMaskIntoConstraints = false
            return button
        }()
    
        override func viewDidLoad() {
            super.viewDidLoad()
    
            // create an action selector when user tap shoot button
            shootButton.addTarget(self, action: #selector(shoot), for: .touchUpInside)   
        }
    
        // example shoot function
        func shoot() {
            // example delay time 3 second then shoot
            let delayTime = 3.0
    
            // delay a shoot after 3 second
            CustomDelay.cd.runAfterDelay(delayTime) {
                // your shoot method here
                // Update your UI here, u don't need to worry to bring this to the main thread because your CustomDelay already make this to main thread automatically :)
            }
        }   
    }
    
  • 1

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

    NSObject+Blocks.h

    #import <Foundation/Foundation.h>
    
    @interface NSObject (Blocks)
    
    - (void)performBlock:(void (^)())block afterDelay:(NSTimeInterval)delay;
    
    @end
    

    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
    

    并使用如下:

    [anyObject performBlock:^{
        [anotherObject doYourThings:stuff];
    } afterDelay:0.15];
    
  • 57

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

    enter image description here

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

    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)")
    }
    

    斯威夫特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)")
    }
    

    目标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);
    });
    
  • 5

    以下是在Swift延迟后触发块的方法:

    runThisAfterDelay(seconds: 2) { () -> () in
        print("Prints this 2 seconds later in main queue")
    }
    
    /// EZSwiftExtensions
    func runThisAfterDelay(seconds seconds: Double, after: () -> ()) {
        let time = dispatch_time(DISPATCH_TIME_NOW, Int64(seconds * Double(NSEC_PER_SEC)))
        dispatch_after(time, dispatch_get_main_queue(), after)
    }
    

    它包含在my repo中作为标准函数 .

  • 5

    我想你正在寻找 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);
    });
    

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

  • 474

    也许比通过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];
    }
    

    所以使用:

    [Util runAfterDelay:2 block:^{
        NSLog(@"two seconds later!");
    }];
    
  • 0

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

  • 1

    在swift 3中,我们可以简单地使用DispatchQueue.main.asyncAfter函数在'n'秒延迟后触发任何函数或动作 . 在代码中,我们在1秒后设置了延迟 . 您可以调用此函数体内的任何函数,该函数将在延迟1秒后触发 .

    let when = DispatchTime.now() + 1
    DispatchQueue.main.asyncAfter(deadline: when) {
    
        // Trigger the function/action after the delay of 1Sec
    
    }
    
  • 1

    这是一个 handy helper ,以防止反复拨打恼人的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)
            }
        }
    }
    

    现在你只需 delay your code on the Main thread 就像这样:

    delay(bySeconds: 1.5) { 
        // delayed code
    }
    

    如果你想 delay your code to different thread

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

    如果您更喜欢 Framework ,它还有一些更方便的功能,那么请查看 HandySwift . 您可以将它添加到项目 via Carthage 然后使用它与上面的示例完全相同:

    import HandySwift    
    
    delay(bySeconds: 1.5) { 
        // delayed code
    }
    
  • 16

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

    DispatchQueue.main.asyncAfter(
      DispatchTime.now() + DispatchTimeInterval.seconds(2)) {
        // do work
    }
    

相关问题