Câu trả lời:
Tôi nghĩ rằng bạn đang tìm kiếm dispatch_after()
. Nó yêu cầu khối của bạn chấp nhận không có tham số, nhưng bạn chỉ có thể để khối bắt các biến đó từ phạm vi cục bộ của bạn.
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);
});
Thêm: https://developer.apple.com/documentation/dispatch/1452876-dispatch_after
dispatch_time(DISPATCH_TIME_NOW, 10ull * NSEC_PER_SEC)
đoạn là khó chịu. Không có cách nào sạch hơn cho việc này sao?
dispatch_get_current_queue()
luôn trả về hàng đợi mà mã đang được chạy. Vì vậy, khi mã này được chạy từ luồng chính, khối cũng sẽ được thực thi trên luồng chính.
dispatch_get_current_queue()
hiện không được chấp nhận
Bạn có thể sử dụng dispatch_after
để gọi một khối sau. Trong Xcode, bắt đầu nhập dispatch_after
và nhấn Enter
để tự động hoàn thành theo các bước sau:
Đây là một ví dụ với hai số float là "đối số". Bạn không cần phải dựa vào bất kỳ loại macro nào và mục đích của mã là khá rõ ràng:
let time1 = 8.23
let time2 = 3.42
// Delay 2 seconds
DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) {
print("Sum of times: \(time1 + time2)")
}
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)")
}
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);
});
NSEC_PER_SEC * 0.5
sẽ làm việc như NSEC_PER_MSEC * 500
. Mặc dù bạn đúng khi lưu ý rằng dispatch_time
sẽ có số nguyên 64 bit, giá trị mà nó mong đợi là tính bằng nano giây. NSEC_PER_SEC
được định nghĩa là 1000000000ull
và nhân nó với hằng số dấu phẩy động 0.5
sẽ thực hiện ngầm định số học dấu phẩy động, năng suất 500000000.0
, trước khi nó được truyền lại một cách rõ ràng thành số nguyên 64 bit. Vì vậy, nó hoàn toàn chấp nhận được để sử dụng một phần nhỏ NSEC_PER_SEC
.
Làm thế nào về việc sử dụng thư viện đoạn mã tích hợp Xcode?
Cập nhật cho Swift:
Nhiều phiếu bầu đã truyền cảm hứng cho tôi để cập nhật câu trả lời này.
Thư viện đoạn mã Xcode tích dispatch_after
hợp chỉ dành cho objective-c
ngôn ngữ. Mọi người cũng có thể tạo Đoạn mã tùy chỉnh của riêng mình cho Swift
.
Viết cái này trong 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#>
})
Kéo mã này và thả nó vào khu vực thư viện đoạn mã.
Cuối danh sách đoạn mã, sẽ có một thực thể mới được đặt tên My Code Snippet
. Chỉnh sửa này cho một tiêu đề. Đối với đề xuất khi bạn nhập Xcode điền vào Completion Shortcut
.
Để biết thêm thông tin, xem CreationaCustomCodeSnippet .
Kéo mã này và thả nó vào khu vực thư viện đoạn mã.
DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(<#delayInSeconds#>)) {
<#code to be executed after a specified delay#>
}
Mở rộng câu trả lời của Jaime Cham Tôi đã tạo một danh mục NSObject + Blocks như bên dưới. Tôi cảm thấy các phương thức này phù hợp hơn với các performSelector:
phương thức NSObject hiện có
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
và sử dụng như vậy:
[anyObject performBlock:^{
[anotherObject doYourThings:stuff];
} afterDelay:0.15];
delay
nên có NSTimeInterval
(mà là một double
). #import <UIKit/UIKit.h>
không cần thiết Và, tôi không thấy lý do tại sao - (void)performBlock:(void (^)())block;
có thể hữu ích, vì vậy có thể được xóa khỏi tiêu đề.
Có lẽ đơn giản hơn là đi qua GCD, trong một lớp ở đâu đó (ví dụ "Util") hoặc Danh mục trên Đối tượng:
+ (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];
}
Vì vậy, để sử dụng:
[Util runAfterDelay:2 block:^{
NSLog(@"two seconds later!");
}];
Đối với Swift, tôi đã tạo một hàm toàn cầu, không có gì đặc biệt, bằng cách sử dụng dispatch_after
phương thức. Tôi thích điều này hơn vì nó dễ đọc và dễ sử dụng:
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)
}
Mà bạn có thể sử dụng như sau:
performBlock({ () -> Void in
// Perform actions
}, afterDelay: 0.3)
after
. Sau đó, bạn có thể viết:after(2.0){ print("do somthing") }
Đây là 2 xu = 5 phương pháp của tôi;)
Tôi thích gói gọn các chi tiết này và yêu cầu AppCode cho tôi biết cách hoàn thành câu của mình.
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);
}
PerformanceSelector: WithObject luôn lấy một đối tượng, vì vậy để truyền các đối số như int / double / float, v.v ..... Bạn có thể sử dụng một cái gì đó như thế này.
// NSNumber là một đối tượng ..
[self performSelector:@selector(setUserAlphaNumber:)
withObject: [NSNumber numberWithFloat: 1.0f]
afterDelay:1.5];
-(void) setUserAlphaNumber: (NSNumber*) number{
[txtUsername setAlpha: [number floatValue] ];
}
Tương tự như cách bạn có thể sử dụng [NSNumber numberWithInt:] vv .... và trong phương thức nhận, bạn có thể chuyển đổi số thành định dạng của mình dưới dạng [số int] hoặc [số nhân đôi].
Hàm Clark_after gửi một đối tượng khối tới hàng đợi công văn sau một khoảng thời gian nhất định. Sử dụng mã dưới đây để thực hiện một số taks liên quan đến giao diện người dùng sau 2,0 giây.
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")
})
Trong 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: {
})
mainQueue,
thay vìmainQueue)
Đây là một trợ giúp hữu ích để ngăn chặn cuộc gọi GCD phiền phức lặp đi lặp lại:
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)
}
}
}
Bây giờ bạn chỉ cần trì hoãn mã của mình trên luồng chính như thế này:
delay(bySeconds: 1.5) {
// delayed code
}
Nếu bạn muốn trì hoãn mã của mình đến các luồng khác nhau :
delay(bySeconds: 1.5, dispatchLevel: .background) {
// delayed code that will run on background thread
}
Nếu bạn thích một Framework cũng có một số tính năng tiện dụng hơn thì hãy kiểm tra HandySwift . Bạn có thể thêm nó vào dự án của mình thông qua Carthage sau đó sử dụng chính xác như trong các ví dụ trên:
import HandySwift
delay(bySeconds: 1.5) {
// delayed code
}
Trong swift 3, chúng ta chỉ cần sử dụng hàm DispatchQueue.main.asyncAfter để kích hoạt bất kỳ chức năng hoặc hành động nào sau thời gian trễ là 'giây'. Ở đây trong mã chúng tôi đã đặt độ trễ sau 1 giây. Bạn gọi bất kỳ chức năng nào bên trong cơ thể của chức năng này sẽ kích hoạt sau độ trễ 1 giây.
let when = DispatchTime.now() + 1
DispatchQueue.main.asyncAfter(deadline: when) {
// Trigger the function/action after the delay of 1Sec
}
Bạn có thể bao bọc đối số trong lớp của riêng bạn hoặc bọc cuộc gọi phương thức trong một phương thức không cần truyền vào kiểu nguyên thủy. Sau đó gọi phương thức đó sau khi bạn trì hoãn và trong phương thức đó thực hiện bộ chọn bạn muốn thực hiện.
Đây là cách bạn có thể kích hoạt một khối sau khi trì hoãn trong 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)
}
Nó bao gồm như là một chức năng tiêu chuẩn trong repo của tôi .
Swift 3 & Xcode 8.3.2
Mã này sẽ giúp bạn, tôi cũng thêm một lời giải thích
// 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 :)
}
}
}
Tôi tin rằng tác giả không hỏi làm thế nào để chờ thời gian phân đoạn (độ trễ), mà thay vào đó làm thế nào để vượt qua vô hướng như đối số của bộ chọn (withObject :) và cách nhanh nhất trong mục tiêu C hiện đại là:
[obj performSelector:... withObject:@(0.123123123) afterDelay:10]
bộ chọn của bạn phải thay đổi tham số của nó thành NSNumber và truy xuất giá trị bằng cách sử dụng bộ chọn như floatValue hoặc doubleValue