Yanıtlar:
Bence aradığınızı dispatch_after()
. Bloğunuzun parametre kabul etmemesi gerekir, ancak bloğun bu değişkenleri yerel kapsamınızdan yakalamasına izin verebilirsiniz.
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);
});
Daha fazla: https://developer.apple.com/documentation/dispatch/1452876-dispatch_after
dispatch_time(DISPATCH_TIME_NOW, 10ull * NSEC_PER_SEC)
pasaj kötü. Bunun daha temiz bir yolu yok mu?
dispatch_get_current_queue()
her zaman kodun çalıştırıldığı kuyruğu döndürür. Dolayısıyla bu kod ana evreden çalıştırıldığında, blok ana evrede de yürütülür.
dispatch_get_current_queue()
şimdi kullanımdan kaldırıldı
Daha dispatch_after
sonra bir bloğu aramak için kullanabilirsiniz . Xcode'da yazmaya başlayın dispatch_after
ve Enter
aşağıdakileri otomatik olarak tamamlamak için tuşuna basın:
İşte "değişken" olarak iki kayan noktalı bir örnek. Herhangi bir makro türüne güvenmeniz gerekmez ve kodun amacı oldukça açıktır:
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
aynı şekilde çalışır NSEC_PER_MSEC * 500
. dispatch_time
64 bitlik bir tam sayı beklediğini unutmayın, ancak beklediği değer nanosaniye cinsindendir. NSEC_PER_SEC
olarak tanımlanır 1000000000ull
ve kayan nokta sabiti ile çarpılması , açıkça 64 bitlik bir tamsayıya dökülmeden önce 0.5
bir kayan nokta aritmetiği gerçekleştirerek örtülü olarak gerçekleştirir 500000000.0
. Yani bir kısmını kullanmak mükemmel kabul edilebilir NSEC_PER_SEC
.
Xcode yerleşik kod snippet kitaplığını kullanmaya ne dersiniz?
Swift için güncelleme:
Birçok oy, bu cevabı güncellemem için bana ilham verdi.
Yerleşik Xcode kod pasajı kitaplığında dispatch_after
yalnızca objective-c
dil vardır. İnsanlar da kendi oluşturabilir Özel Kod Snippet'ini için Swift
.
Bunu Xcode'a yazın.
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#>
})
Bu kodu sürükleyin ve kod snippet kitaplığı alanına bırakın.
Kod pasaj listesinin altında, adlı yeni bir varlık olacaktır My Code Snippet
. Bir başlık için bunu düzenleyin. Öneri olarak Xcode doldururken yazın Completion Shortcut
.
Daha fazla bilgi için bkz. CreatingaCustomCodeSnippet .
Bu kodu sürükleyin ve kod snippet kitaplığı alanına bırakın.
DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(<#delayInSeconds#>)) {
<#code to be executed after a specified delay#>
}
Jaime Cham'nun cevabını genişleterek aşağıdaki gibi bir NSObject + Blocks kategorisi oluşturdum. Bu yöntemlerin mevcut performSelector:
NSObject yöntemleriyle daha iyi eşleştiğini hissettim
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
ve şöyle kullanın:
[anyObject performBlock:^{
[anotherObject doYourThings:stuff];
} afterDelay:0.15];
delay
Arasında olmalıdır NSTimeInterval
: (a olan double
). #import <UIKit/UIKit.h>
Gerek yok. Ve neden - (void)performBlock:(void (^)())block;
yararlı olabileceğini bilmiyorum , bu yüzden başlıktan çıkarılabilir.
Belki bir yerde bir sınıfta (örneğin "Util") ya da Nesne Kategorisinde GCD'den daha basit:
+ (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];
}
Yani kullanmak için:
[Util runAfterDelay:2 block:^{
NSLog(@"two seconds later!");
}];
Swift için dispatch_after
yöntemi kullanarak özel bir şey olmayan küresel bir işlev oluşturdum . Okunabilir ve kullanımı kolay olduğu için bunu daha çok seviyorum:
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)
}
Aşağıdaki gibi kullanabilirsiniz:
performBlock({ () -> Void in
// Perform actions
}, afterDelay: 0.3)
after
. Sonra yazabilirsiniz:after(2.0){ print("do somthing") }
İşte benim 2 sent = 5 yöntem;)
Bu ayrıntıları kapsüllemeyi seviyorum ve AppCode'un cümlelerimi nasıl bitireceğimi söylemesini istiyorum.
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 her zaman bir nesne alır, bu yüzden int / double / float vb gibi argümanları iletmek için ..... Böyle bir şey kullanabilirsiniz.
// NSNumber bir nesnedir ..
[self performSelector:@selector(setUserAlphaNumber:)
withObject: [NSNumber numberWithFloat: 1.0f]
afterDelay:1.5];
-(void) setUserAlphaNumber: (NSNumber*) number{
[txtUsername setAlpha: [number floatValue] ];
}
Aynı şekilde [NSNumber numberWithInt:] etc .... 'yi kullanabilirsiniz ve alma yönteminde sayıyı [number int] veya [number double] biçiminize dönüştürebilirsiniz.
Dispatch_after işlevi, belirli bir süreden sonra bir blok nesnesini bir gönderme kuyruğuna gönderir. 2.0 saniye sonra kullanıcı arayüzü ile ilgili bazı görevleri gerçekleştirmek için aşağıdaki kodu kullanın.
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")
})
Hızlı 3.0'da:
let dispatchTime: DispatchTime = DispatchTime.now() + Double(Int64(2.0 * Double(NSEC_PER_SEC))) / Double(NSEC_PER_SEC)
DispatchQueue.main.asyncAfter(deadline: dispatchTime, execute: {
})
mainQueue,
yerinemainQueue)
İşte can sıkıcı GCD aramasını tekrar tekrar yapmayı önlemek için kullanışlı bir yardımcı :
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)
}
}
}
Şimdi sadece ana iş parçacığındaki kodunuzu şu şekilde geciktirirsiniz :
delay(bySeconds: 1.5) {
// delayed code
}
Kodunuzu farklı bir iş parçacığına ertelemek istiyorsanız :
delay(bySeconds: 1.5, dispatchLevel: .background) {
// delayed code that will run on background thread
}
Bazı daha kullanışlı özelliklere sahip bir Çerçeveyi tercih ederseniz , HandySwift'i kontrol edin . Bunu Carthage aracılığıyla projenize ekleyebilir ve yukarıdaki örneklerde olduğu gibi kullanabilirsiniz:
import HandySwift
delay(bySeconds: 1.5) {
// delayed code
}
Hızlı 3'te, 'n' saniye gecikmesinden sonra herhangi bir işlevi veya eylemi tetiklemek için DispatchQueue.main.asyncAfter işlevini kullanabiliriz. Burada kodda 1 saniye sonra gecikme ayarladık. Bu işlevin gövdesi içinde, 1 saniye gecikmeden sonra tetiklenecek herhangi bir işlevi çağırırsınız.
let when = DispatchTime.now() + 1
DispatchQueue.main.asyncAfter(deadline: when) {
// Trigger the function/action after the delay of 1Sec
}
Bağımsız değişkeni kendi sınıfınızda ya da yöntem çağrısını ilkel türden geçirilmesi gerekmeyen bir yöntemle sarabilirsiniz. Ardından, gecikmeden sonra bu yöntemi çağırın ve bu yöntemde gerçekleştirmek istediğiniz seçiciyi gerçekleştirin.
Swift'te bir gecikmeden sonra bir bloğu nasıl tetikleyebileceğiniz aşağıda açıklanmıştır:
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)
}
Benim repo standart bir işlev olarak dahil .
Swift 3 ve Xcode 8.3.2
Bu kod size yardımcı olacak, ben de bir açıklama ekleyin
// 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 :)
}
}
}
Yazarın kesirli bir süre (gecikme) nasıl bekleyeceğini değil, bunun yerine bir skalayı seçicinin (withObject :) argümanı olarak nasıl geçireceğini ve modern objektif C'deki en hızlı yolun:
[obj performSelector:... withObject:@(0.123123123) afterDelay:10]
seçicinizin parametresini NSNumber olarak değiştirmesi ve floatValue veya doubleValue gibi bir seçici kullanarak değeri alması gerekir