-PerformSelector: withObject: afterDelay :? gibi bir gecikmeden sonra bir bloğu nasıl tetiklersiniz?


735

Bir gecikmeden sonra ilkel parametre ile bir bloğu çağırmanın bir yolu var mı, kullanmak gibi performSelector:withObject:afterDelay:ama int/ double/ gibi bir argümanla float?


Bu, GCD'nin NSOperation'ın yapamayacağı bir şey yapabileceği nadir bir nokta değil mi?
Anonim Beyaz

Yanıtlar:


1175

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


88
Aslında bu doğru değil. __Block deposunda olduğu işaretlenmeyen bir blok tarafından yakalanan nesneler blok tarafından korunur ve yok edildiğinde (tutma sayısı 0 olduğunda) blok tarafından serbest bırakılır. İşte bununla ilgili belgeler: developer.apple.com/library/mac/documentation/Cocoa/Conceptual/…
Ryan

9
bu dispatch_time(DISPATCH_TIME_NOW, 10ull * NSEC_PER_SEC)pasaj kötü. Bunun daha temiz bir yolu yok mu?
samvermette

7
Evet, 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.
Ryan

20
dispatch_get_current_queue()şimdi kullanımdan kaldırıldı
Matej

9
Milisaniye belirtmek istemeniz durumunda NSEC_PER_SEC'in yanı sıra NSEC_PER_MSEC de mevcuttur;)
cprcrack

504

Daha dispatch_aftersonra bir bloğu aramak için kullanabilirsiniz . Xcode'da yazmaya başlayın dispatch_afterve Enteraşağıdakileri otomatik olarak tamamlamak için tuşuna basın:

resim açıklamasını buraya girin

İş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:

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

Hızlı 2

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

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

45
Dikkatli olun gecikme süresi bir çift değildir. Bu yüzden sadece yarım saniye NSEC_PER_SEC * 0.5'i denemeyin. Milisaniyeye düşmeniz ve NSEC_PER_MSEC * 500 kullanmanız gerekir. Bu nedenle, kod örneğinizi insanların NSEC_PER_SEC kesirlerini kullanamayacağını göstermek için int delayInSeconds = 2 olarak değiştirmeniz gerekir.
malhal

11
@malhal Aslında, NSEC_PER_SEC * 0.5aynı şekilde çalışır NSEC_PER_MSEC * 500. dispatch_time64 bitlik bir tam sayı beklediğini unutmayın, ancak beklediği değer nanosaniye cinsindendir. NSEC_PER_SEColarak tanımlanır 1000000000ullve kayan nokta sabiti ile çarpılması , açıkça 64 bitlik bir tamsayıya dökülmeden önce 0.5bir 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.
junjie

202

Xcode yerleşik kod snippet kitaplığını kullanmaya ne dersiniz?

resim açıklamasını buraya girin

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_afteryalnızca objective-cdil 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. resim açıklamasını buraya girin

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 .

Swift 3'ü Güncelle

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#>
}

19
Xcode'da gerçekten bu özelliği kullanan var mı? Ben sadece kod önerileri pop-up olarak yazmayı tercih ve kullanımı kadar kolay.
Supertecnoboff

6
Ben sadece kopyala & yapıştır kodlamak için en kolay yolu olduğunu düşündüm kadar. Şimdi sadece sürükle ve bırak .... hahaha
arshu

58

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];

5
delayArası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.
anlamı önemli

@ anlamı önemli, her ikisi de geçerli puan +1, cevabımı buna göre güncelledim.
Oliver Pearmain

bu hiç doğru değil, performSelector'ın dealloc'da açıkça kaldırılması gerekiyor, aksi takdirde gerçekten garip davranış ve çökmelerle karşılaşacaksınız, daha doğru dispatch_after'ı kullanmaktır
Peter Lapisu

21

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

3
@Jaimie Cham Sizce GCD'den geçmenin zor olduğunu düşünüyorsunuz?
Besi

2
GCD'den geçmek PerformSelector: afterDelay: 'dan biraz farklı bir davranışa sahiptir, bu nedenle GCD'yi kullanmamanın nedenleri olabilir. Örneğin, aşağıdaki soruya bakın: stackoverflow.com/questions/10440412/…
fishin27

PerformSelector'a geçirmeden önce neden bloğu kopyalıyorsunuz?
c roald

1
Gecikme için üzgünüm. @croald: Bloğu yığından öbeğe taşımak için kopyaya ihtiyacınız olduğunu düşünüyorum.
Jaime Cham

@Besi: daha garip ve amacı gizler.
Jaime Cham

21

Swift için dispatch_afteryö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)

1
Argümanları değiştirmeyi ve yeniden adlandırmayı öneririm after. Sonra yazabilirsiniz:after(2.0){ print("do somthing") }
Lars Blumberg

16

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

8

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.


8

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

          })

bir yazım hatası var: mainQueue, yerinemainQueue)
Bastian

5

İşte bir gecikmeden sonra işi sıraya almanın Swift 3 yolu.

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

5

İş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
}

Bu, gecikme işlevinizin arka plan iş parçacığından kod yürütmesi anlamına gelir. Örneğinizi kullanan bir kişi, // gecikmeli kod bölümüne UI ile ilgili herhangi bir kod koyarsa, uygulamanın çökmesine hata ayıklamak gerçekten zor zamanlara sahip olabilir .
nalexn

Varsayılan olarak benim yöntem ana iş parçacığı kullanır, böylece olmamalı. .Main?
Jeehut


4

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

}

4

Xcode 10.2 ve Swift 5 ve üstü

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

1

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.


1

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 .


1

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

0

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

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.