มีวิธีการเรียกบล็อกพารามิเตอร์ดั้งเดิมหลังจากที่ล่าช้าเช่นการใช้performSelector:withObject:afterDelay:
แต่มีข้อโต้แย้งเช่นint
/ double
/ float
?
มีวิธีการเรียกบล็อกพารามิเตอร์ดั้งเดิมหลังจากที่ล่าช้าเช่นการใช้performSelector:withObject:afterDelay:
แต่มีข้อโต้แย้งเช่นint
/ double
/ float
?
คำตอบ:
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
dispatch_time(DISPATCH_TIME_NOW, 10ull * NSEC_PER_SEC)
ข้อมูลโค้ดเป็นที่น่ารังเกียจ มีวิธีที่สะอาดกว่านี้หรือไม่?
dispatch_get_current_queue()
ส่งคืนคิวที่โค้ดกำลังรันอยู่เสมอ ดังนั้นเมื่อเรียกใช้รหัสนี้จากเธรดหลักบล็อกจะถูกดำเนินการบนเธรดหลัก
dispatch_get_current_queue()
เลิกใช้แล้วในตอนนี้
คุณสามารถใช้dispatch_after
โทรหาบล็อกได้ในภายหลัง ใน Xcode เริ่มพิมพ์dispatch_after
และกดEnter
เพื่อเติมข้อความอัตโนมัติต่อไปนี้:
นี่คือตัวอย่างที่มีสองการลอยเป็น "อาร์กิวเมนต์" คุณไม่จำเป็นต้องพึ่งพามาโครประเภทใด ๆ และเจตนาของโค้ดนั้นค่อนข้างชัดเจน:
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
NSEC_PER_MSEC * 500
ในขณะที่คุณถูกต้องที่จะทราบว่าdispatch_time
คาดว่าจะเป็นจำนวนเต็ม 64 บิตค่าที่คาดว่าจะอยู่ในหน่วยนาโนวินาที NSEC_PER_SEC
ถูกกำหนดให้เป็น1000000000ull
และคูณด้วยค่าคงที่จุดลอยตัว0.5
โดยปริยายจะทำการคำนวณเลขทศนิยมโดยปริยายยอม500000000.0
ก่อนที่มันจะถูกส่งกลับอย่างชัดเจนเป็นจำนวนเต็ม 64 บิต NSEC_PER_SEC
ดังนั้นจึงเป็นที่ยอมรับอย่างสมบูรณ์ที่จะใช้ส่วนของ
วิธีการเกี่ยวกับการใช้โค้ดตัวอย่างโค้ดในตัวของ Xcode?
อัปเดตสำหรับ Swift:
การโหวตหลายครั้งเป็นแรงบันดาลใจให้ฉันอัปเดตคำตอบนี้
โค้ดตัวอย่างโค้ด Xcode ในตัวมีdispatch_after
เฉพาะobjective-c
ภาษาเท่านั้น ผู้คนยังสามารถสร้างCustom Code Snippetของ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
ใน
สำหรับข้อมูลเพิ่มเติมโปรดดูที่CreatingaCustomCodeSnippet
ลากรหัสนี้แล้ววางในพื้นที่ไลบรารีโค้ด
DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(<#delayInSeconds#>)) {
<#code to be executed after a specified delay#>
}
การเพิ่มคำตอบของ 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];
delay
ควรจะเป็นNSTimeInterval
(ซึ่งเป็นdouble
) #import <UIKit/UIKit.h>
ไม่จำเป็น และฉันไม่เห็นสาเหตุที่- (void)performBlock:(void (^)())block;
อาจเป็นประโยชน์ดังนั้นสามารถลบออกจากส่วนหัวได้
อาจง่ายกว่าการไปถึง 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!");
}];
สำหรับ 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)
after
ฉันขอแนะนำให้ข้อโต้แย้งแลกเปลี่ยนและเปลี่ยนชื่อใหม่เพื่อ จากนั้นคุณสามารถเขียน:after(2.0){ print("do somthing") }
นี่คือ 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);
}
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:] ฯลฯ ... และในวิธีการรับคุณสามารถแปลงตัวเลขให้อยู่ในรูปแบบของคุณเป็น [number int] หรือ [number double]
ฟังก์ชั่น dispatch_after ส่งวัตถุบล็อกไปยังคิวการจัดส่งหลังจากระยะเวลาที่กำหนด ใช้รหัสด้านล่างเพื่อดำเนินการกับ taks ที่เกี่ยวข้องกับ UI หลังจาก 2.0 วินาที
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")
})
ในสวิฟท์ 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,
แทนmainQueue)
นี่คือวิธีที่รวดเร็วในการคิวงานหลังจากล่าช้า
DispatchQueue.main.asyncAfter(
DispatchTime.now() + DispatchTimeInterval.seconds(2)) {
// do work
}
ต่อไปนี้เป็นผู้ช่วยที่มีประโยชน์เพื่อป้องกันการโทร 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 คุณสามารถเพิ่มลงในโครงการของคุณผ่านทาง Carthageจากนั้นใช้เหมือนในตัวอย่างด้านบน:
import HandySwift
delay(bySeconds: 1.5) {
// delayed code
}
ใน 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
}
Xcode 10.2 และ Swift 5 ขึ้นไป
DispatchQueue.main.asyncAfter(deadline: .now() + 2, execute: {
// code to execute
})
คุณสามารถตัดอาร์กิวเมนต์ในคลาสของคุณเองหรือตัดการเรียกเมธอดในวิธีที่ไม่จำเป็นต้องถูกส่งในรูปแบบดั้งเดิม จากนั้นเรียกวิธีการนั้นหลังจากที่คุณล่าช้าและภายในวิธีนั้นให้เลือกตัวเลือกที่คุณต้องการ
นี่คือวิธีที่คุณสามารถเรียกบล็อกหลังจากการหน่วงเวลาใน 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)
}
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 :)
}
}
}
ฉันเชื่อว่าผู้เขียนไม่ได้ถามว่าจะรอเวลาแบบเศษส่วน (ล่าช้า) ได้อย่างไร แต่จะส่งวิธีเซนต์คิตส์และเนวิสเป็นอาร์กิวเมนต์ของตัวเลือก (withObject :) และวิธีที่เร็วที่สุดในวัตถุประสงค์ C สมัยใหม่คือ:
[obj performSelector:... withObject:@(0.123123123) afterDelay:10]
ตัวเลือกของคุณต้องเปลี่ยนพารามิเตอร์เป็น NSNumber และดึงค่าโดยใช้ตัวเลือกเช่น floatValue หรือ doubleValue