Apakah ada cara untuk memanggil blok dengan parameter primitif setelah penundaan, seperti menggunakan performSelector:withObject:afterDelay:
tetapi dengan argumen seperti int
/ double
/ float
?
Apakah ada cara untuk memanggil blok dengan parameter primitif setelah penundaan, seperti menggunakan performSelector:withObject:afterDelay:
tetapi dengan argumen seperti int
/ double
/ float
?
Jawaban:
Saya pikir Anda sedang mencari dispatch_after()
. Ini membutuhkan blok Anda untuk tidak menerima parameter, tetapi Anda bisa membiarkan blok menangkap variabel-variabel itu dari lingkup lokal Anda.
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);
});
Lebih lanjut: https://developer.apple.com/documentation/dispatch/1452876-dispatch_after
dispatch_time(DISPATCH_TIME_NOW, 10ull * NSEC_PER_SEC)
potongan jahat. Apakah tidak ada cara yang lebih bersih untuk ini?
dispatch_get_current_queue()
selalu mengembalikan antrean tempat kode dijalankan. Jadi ketika kode ini dijalankan dari utas utama, blok juga akan dieksekusi pada utas utama.
dispatch_get_current_queue()
sudah ditinggalkan sekarang
Anda dapat menggunakan dispatch_after
untuk memanggil blok nanti. Di Xcode, mulailah mengetik dispatch_after
dan tekan Enter
untuk lengkapi otomatis sebagai berikut:
Inilah contoh dengan dua pelampung sebagai "argumen." Anda tidak harus bergantung pada jenis makro apa pun, dan maksud kode itu cukup jelas:
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
akan bekerja sama dengan NSEC_PER_MSEC * 500
. Meskipun Anda benar untuk mencatat bahwa dispatch_time
mengharapkan integer 64-bit, nilai yang diharapkan dalam nanodetik. NSEC_PER_SEC
didefinisikan sebagai 1000000000ull
, dan mengalikannya dengan konstanta floating-point 0.5
secara implisit akan melakukan aritmatika floating-point, menghasilkan 500000000.0
, sebelum secara eksplisit dilemparkan kembali ke integer 64-bit. Jadi bisa diterima menggunakan sebagian kecil dari NSEC_PER_SEC
.
Bagaimana dengan menggunakan pustaka cuplikan kode bawaan Xcode?
Pembaruan untuk Swift:
Banyak suara menginspirasi saya untuk memperbarui jawaban ini.
Pustaka cuplikan kode Xcode bawaan dispatch_after
hanya untuk objective-c
bahasa. Orang juga dapat membuat Cuplikan Kode Kustom untuk Swift
.
Tulis ini dalam 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#>
})
Seret kode ini dan letakkan di area pustaka potongan kode.
Di bawah daftar cuplikan kode, akan ada entitas baru bernama My Code Snippet
. Edit ini untuk judul. Untuk saran saat Anda mengetik Xcode, isikan Completion Shortcut
.
Untuk info lebih lanjut, lihat CreatingaCustomCodeSnippet .
Seret kode ini dan letakkan di area pustaka potongan kode.
DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(<#delayInSeconds#>)) {
<#code to be executed after a specified delay#>
}
Memperluas jawaban Jaime Cham, saya membuat kategori + Blok NSObject seperti di bawah ini. Saya merasa metode ini lebih cocok dengan performSelector:
metode NSObject yang ada
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
dan gunakan seperti ini:
[anyObject performBlock:^{
[anotherObject doYourThings:stuff];
} afterDelay:0.15];
delay
harus dari NSTimeInterval
(yang merupakan double
). #import <UIKit/UIKit.h>
tidak dibutuhkan. Dan, saya tidak mengerti mengapa ini - (void)performBlock:(void (^)())block;
bisa berguna, jadi bisa dihapus dari header.
Mungkin lebih sederhana daripada melalui GCD, di kelas di suatu tempat (misalnya "Util"), atau Kategori di Obyek:
+ (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];
}
Jadi untuk digunakan:
[Util runAfterDelay:2 block:^{
NSLog(@"two seconds later!");
}];
Untuk Swift, saya telah membuat fungsi global, tidak ada yang istimewa, menggunakan dispatch_after
metode ini. Saya lebih suka ini karena mudah dibaca dan mudah digunakan:
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)
}
Yang bisa Anda gunakan sebagai berikut:
performBlock({ () -> Void in
// Perform actions
}, afterDelay: 0.3)
after
. Maka Anda dapat menulis:after(2.0){ print("do somthing") }
Inilah 2 sen saya = 5 metode;)
Saya suka merangkum detail-detail ini dan minta AppCode memberi tahu saya cara menyelesaikan kalimat saya.
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 selalu mengambil objek, jadi untuk memberikan argumen seperti int / double / float dll ..... Anda dapat menggunakan sesuatu seperti ini.
// NSNumber adalah objek ..
[self performSelector:@selector(setUserAlphaNumber:)
withObject: [NSNumber numberWithFloat: 1.0f]
afterDelay:1.5];
-(void) setUserAlphaNumber: (NSNumber*) number{
[txtUsername setAlpha: [number floatValue] ];
}
Dengan cara yang sama Anda dapat menggunakan [NSNumber numberWithInt:] dll .... dan dalam metode penerima Anda dapat mengonversi angka ke dalam format Anda sebagai [number int] atau [number double].
Fungsi dispatch_after mengirimkan objek blok ke antrian pengiriman setelah periode waktu tertentu. Gunakan kode di bawah ini untuk melakukan beberapa pengambilan terkait UI setelah 2,0 detik.
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")
})
Di cepat 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,
bukannyamainQueue)
Berikut ini adalah bantuan praktis untuk mencegah panggilan GCD yang menjengkelkan berulang kali:
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)
}
}
}
Sekarang Anda cukup menunda kode Anda di utas utama seperti ini:
delay(bySeconds: 1.5) {
// delayed code
}
Jika Anda ingin menunda kode Anda ke utas berbeda :
delay(bySeconds: 1.5, dispatchLevel: .background) {
// delayed code that will run on background thread
}
Jika Anda lebih suka Kerangka yang juga memiliki beberapa fitur yang lebih berguna maka checkout HandySwift . Anda dapat menambahkannya ke proyek Anda melalui Carthage lalu menggunakannya persis seperti pada contoh di atas:
import HandySwift
delay(bySeconds: 1.5) {
// delayed code
}
Di swift 3, Kita cukup menggunakan fungsi DispatchQueue.main.asyncAfter untuk memicu fungsi atau tindakan apa pun setelah penundaan 'n' detik. Di sini, dalam kode, kami telah menetapkan penundaan setelah 1 detik. Anda memanggil fungsi apa pun di dalam tubuh fungsi ini yang akan memicu setelah penundaan 1 detik.
let when = DispatchTime.now() + 1
DispatchQueue.main.asyncAfter(deadline: when) {
// Trigger the function/action after the delay of 1Sec
}
Anda bisa membungkus argumen di kelas Anda sendiri, atau membungkus pemanggilan metode dalam metode yang tidak perlu diteruskan dalam tipe primitif. Kemudian panggil metode itu setelah penundaan Anda, dan di dalam metode itu lakukan pemilih yang ingin Anda lakukan.
Inilah cara Anda dapat memicu blok setelah penundaan di 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)
}
Termasuk sebagai fungsi standar dalam repo saya .
Swift 3 & Xcode 8.3.2
Kode ini akan membantu Anda, saya juga menambahkan penjelasan
// 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 :)
}
}
}
Saya percaya penulis tidak bertanya bagaimana menunggu waktu fraksional (penundaan), melainkan bagaimana cara melewatkan skalar sebagai argumen pemilih (withObject :) dan cara tercepat dalam tujuan modern C adalah:
[obj performSelector:... withObject:@(0.123123123) afterDelay:10]
pemilih Anda harus mengubah parameternya ke NSNumber, dan mengambil nilainya menggunakan pemilih seperti floatValue atau doubleValue