如何在延迟后触发块,例如-performSelector: with Object:的延迟:?

是否有办法在延迟后使用原始参数调用块,例如使用performSelector:withObject:afterDelay:但使用int/double/float等参数?

385723 次浏览

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

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

也许比通过GCD,在某个地方的类(例如“Util”)或Object上的类别更简单:

+ (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!");
}];

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

Swift2

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

在BlocksKit框架中有一个不错的。

BlocksKit

(和班级)

BBlocksKit. m

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

NSObject+Block. h

#import <Foundation/Foundation.h>


@interface NSObject (Blocks)


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


@end

NSObject+Block. 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];

PerformSelector:With Object总是接受一个对象,所以为了传递int/Double/浮点数等参数……您可以使用这样的东西。

//NSNumber是一个对象。

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






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


[txtUsername setAlpha: [number floatValue] ];


}

以同样的方式,您可以使用[NSNumber numberWith Int:]等……并且在接收方法中,您可以将数字转换为[numint]或[numDouble]的格式。

以下是我的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);
}

如何使用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#>
})

拖动此代码并将其放入代码片段库区域。 输入图片描述

代码片段列表的底部,将有一个名为My Code Snippet的新实体。编辑此标题。在输入Xcode时填写Completion Shortcut以获取建议。

有关更多信息,请参阅创建自定义代码片段

更新Swift 3

拖动此代码并将其放入代码片段库区域。

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

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

以下是在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)
}

它作为标准函数包含在我的回购中。

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: {


})

这是一个方便的助手,以防止一遍又一遍地制作烦人的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(bySeconds: 1.5) {
// delayed code
}

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

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

如果您更喜欢框架,它也有一些更方便的功能,那么请查看HandySwift。您可以将其添加到您的项目通过迦太基中,然后像上面的示例一样使用它:

import HandySwift


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

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

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

我相信作者不是在问如何等待分数时间(延迟),而是如何将标量作为选择器的参数传递(with Object:),而现代目标C中最快的方法是:

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

您的选择器必须将其参数更改为NSNumber,并使用选择器(如FloatValue或douleValue)检索值

在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


}

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

Xcode 10.2和Swift 5及以上

DispatchQueue.main.asyncAfter(deadline: .now() + 2, execute: {
// code to execute
})

于2021年4月更新为Xcode 12.4。这仍然有效,除了现在+按钮呈现更多图标,包括视图库和修饰符库(见下文),视图可能是默认值。 带有视图和修改器的库图标 所以一定要选择片段库,如下图所示。


(已于2020年6月在Xcode 11.3.1上验证)

Xcode提供了一个代码片段来做到这一点。你只需要输入延迟值和你希望在延迟后运行的代码。

  1. 单击Xcode右上角的+按钮,同时编辑一些代码(而不是在Project Navigator中,其中会显示其他库,如功能)
  2. 确保在可用图标中选择了片段库(请参阅屏幕截图,带有{}的图标)。
  3. 搜索after
  4. 它将只返回1个搜索结果,这是所需的片段(见屏幕截图)。双击它,你就可以走了。

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

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

在此处输入图片描述

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