आप देरी के बाद एक ब्लॉक को कैसे ट्रिगर करते हैं, जैसे -performSelector: withObject: afterDelay ??


735

वहाँ एक रास्ता का उपयोग कर की तरह, एक विलंब के बाद एक आदिम पैरामीटर के साथ एक ब्लॉक कॉल कर सकता performSelector:withObject:afterDelay:है, लेकिन जैसे एक तर्क के साथ int/ double/ float?


यह एक दुर्लभ बिंदु है जहाँ GCD कुछ कर सकता है NSOperation नहीं हो सकता है?
अनाम सफेद

जवाबों:


1174

मुझे लगता है कि आप ढूंढ रहे हैं 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


88
वास्तव में, यह सच नहीं है। ब्लॉक द्वारा कैप्चर की गई वस्तुएं जिन्हें __ब्लॉक स्टोरेज के रूप में चिह्नित नहीं किया गया है, उन्हें ब्लॉक द्वारा बनाए रखा जाता है, और इसे नष्ट होने पर ब्लॉक द्वारा जारी किया जाता है (जब इसकी रिटेन काउंट 0 पर जाती है)। यहाँ उस पर प्रलेखन है: developer.apple.com/library/mac/documentation/Cocoa/Conceptual/…
रयान

9
यह dispatch_time(DISPATCH_TIME_NOW, 10ull * NSEC_PER_SEC)स्निपेट गंदा है। क्या इसके लिए कोई क्लीनर रास्ता नहीं है?
समवृत्त

7
हां, dispatch_get_current_queue()हमेशा उस कतार को लौटाता है जिससे कोड चलाया जा रहा है। इसलिए जब यह कोड मुख्य धागे से चलाया जाता है, तो ब्लॉक को मुख्य धागे पर भी निष्पादित किया जाएगा।
रयान

20
dispatch_get_current_queue()अब पदावनत किया गया है
Matej

9
NSEC_PER_SEC के अलावा, NSEC_PER_MSEC भी मौजूद है, यदि आप इसे निर्दिष्ट करना चाहते हैं;)
cprcrack

504

आप dispatch_afterबाद में किसी ब्लॉक को कॉल करने के लिए उपयोग कर सकते हैं । Xcode में, टाइप करना शुरू करें dispatch_afterऔर Enterनिम्नलिखित के लिए स्वतः पूर्ण करें:

यहां छवि विवरण दर्ज करें

यहां दो तर्कों के साथ एक उदाहरण दिया गया है "तर्क।" आपको किसी भी प्रकार के मैक्रो पर भरोसा करने की आवश्यकता नहीं है, और कोड का इरादा काफी स्पष्ट है:

स्विफ्ट 3, स्विफ्ट 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)")
}

स्विफ्ट 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)")
}

उद्देश्य सी

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
सावधान रहें देरी का समय दोहरा नहीं है। तो बस आधे घंटे के लिए NSEC_PER_SEC * 0.5 की कोशिश मत करो यह काम नहीं करेगा! आपको मिलीसेकंड पर जाने और NSEC_PER_MSEC * 500 का उपयोग करने की आवश्यकता है। इसलिए आपको अपना कोड नमूना बदलना चाहिए: int देरीInSeconds = 2 यह दिखाने के लिए कि लोग NSEC_PER_SEC के अंशों का उपयोग नहीं कर सकते हैं।
मल्हल

11
@ संभल वास्तव में, के NSEC_PER_SEC * 0.5रूप में ही काम करेगा NSEC_PER_MSEC * 500। जब आप ध्यान दें कि dispatch_time64-बिट पूर्णांक की अपेक्षा करता है, तो यह मान लें कि यह अपेक्षा है कि यह नैनोसेकंड में है। NSEC_PER_SECके रूप में परिभाषित किया गया है 1000000000ull, और एक फ़्लोटिंग-पॉइंट स्थिरांक के साथ गुणा 0.5करना, स्पष्ट रूप से फ़्लोटिंग-पॉइंट अंकगणित, पैदावार करना 500000000.0, इससे पहले कि यह स्पष्ट रूप से 64-बिट पूर्णांक में वापस डाली जाए। तो यह एक अंश का उपयोग करने के लिए पूरी तरह से स्वीकार्य है NSEC_PER_SEC
जंजी

202

Xcode बिल्ट-इन कोड स्निपेट लाइब्रेरी का उपयोग करने के बारे में कैसे?

यहां छवि विवरण दर्ज करें

स्विफ्ट के लिए अपडेट करें:

कई वोटों ने मुझे इस उत्तर को अपडेट करने के लिए प्रेरित किया।

बिल्ड-इन Xcode कोड स्निपेट लाइब्रेरी में dispatch_afterकेवल objective-cभाषा के लिए है। लोगों को भी अपने स्वयं के बना सकते हैं कस्टम कोड स्निपेट के लिए 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

अधिक जानकारी के लिए CreateaCustomCodeSnippet देखें ।

अद्यतन स्विफ्ट 3

इस कोड को खींचें और इसे कोड स्निपेट लाइब्रेरी क्षेत्र में छोड़ दें।

DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(<#delayInSeconds#>)) {
    <#code to be executed after a specified delay#>
}

19
क्या कोई वास्तव में Xcode में इस सुविधा का उपयोग करता है? मैं इसे कोड सुझाव पॉपअप के रूप में टाइप करना पसंद करता हूं और उपयोग करने में आसान है।
सुपरटेकनोफॉफ़

6
जब तक मुझे पता है कि मैंने सोचा था कि कॉपी और पेस्ट कोड का सबसे आसान तरीका था। अब मैं सिर्फ ड्रैग एंड ड्रॉप ....
हाहाहा

58

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

5
delayका होना चाहिए NSTimeInterval(जो एक है double)। #import <UIKit/UIKit.h>आवश्यकता नहीं है। और, मैं नहीं देखता कि - (void)performBlock:(void (^)())block;उपयोगी क्यों हो सकता है, इसलिए हेडर से हटाया जा सकता है।
अर्थ-मामलों

@ अर्थ-मामले, दोनों मान्य बिंदु +1, मैंने अपने उत्तर को उसी के अनुसार अद्यतन किया है।
ओलिवर पियरमैन

यह बिल्कुल भी सही नहीं है,
परफॉर्मेक्टर को डीललोक

21

शायद एक कक्षा में जीसीडी के माध्यम से कहीं से भी सरल, (उदाहरण के लिए "यूटिल"), या ऑब्जेक्ट पर एक श्रेणी:

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

3
@ जैमी चम आपको क्यों लगता है कि जीसीडी से गुजरना मुश्किल है?
बेसि

2
GCD के माध्यम से जाने से PerformSelector की तुलना में थोड़ा अलग व्यवहार होता है: afterDelay :, इसलिए PCD का उपयोग नहीं करने के कारण हो सकते हैं। उदाहरण के लिए, निम्नलिखित प्रश्न देखें: stackoverflow.com/questions/10440412/…
मछुआरे

आप प्रदर्शनकर्ता को पास करने से पहले ब्लॉक की नकल क्यों करते हैं?
c रोनाल्ड

1
विलंब के लिए क्षमा चाहते हैं। @ क्रॉल्ड: मुझे लगता है कि आपको स्टैक से ढेर तक ले जाने के लिए कॉपी की जरूरत है।
जयम चाम

@ बासी: अधिक चिंताजनक और इरादे को छुपाता है।
जयम चाम

21

स्विफ्ट के लिए मैंने एक ग्लोबल फंक्शन बनाया है, जो 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)

1
मैं तर्कों को स्वैप करने और उसका नाम बदलने का सुझाव देता हूं after। फिर आप लिख सकते हैं:after(2.0){ print("do somthing") }
लार्स ब्लमबर्ग

16

यहाँ मेरे 2 सेंट = 5 विधियाँ हैं;)

मैं इन विवरणों को इनकैप्सुलेट करना पसंद करता हूं और ऐपकोड बताता है कि मुझे अपने वाक्य कैसे खत्म करने हैं।

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 हमेशा एक ऑब्जेक्ट लेता है, इसलिए 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 numberWithInt:] आदि का उपयोग कर सकते हैं .... और प्राप्त करने की विधि में आप संख्या को अपने प्रारूप में [संख्या int] या [संख्या डबल] के रूप में परिवर्तित कर सकते हैं।


8

Dispatch_after function एक निश्चित समय अवधि के बाद प्रेषण कतार में एक ब्लॉक ऑब्जेक्ट भेजता है। 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)
बैस्टियन

5

यहाँ देरी के बाद काम कतार करने के लिए स्विफ्ट 3 रास्ता है।

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

5

बार-बार कष्टप्रद 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
}

यदि आप एक ऐसी फ्रेमवर्क पसंद करते हैं जिसमें कुछ अधिक उपयोगी विशेषताएं हैं तो हैंडवाइस लिफ्ट चेकआउट करें । आप इसे Carthage के माध्यम से अपनी परियोजना में जोड़ सकते हैं, फिर इसे ऊपर दिए गए उदाहरणों की तरह उपयोग कर सकते हैं:

import HandySwift    

delay(bySeconds: 1.5) { 
    // delayed code
}

इसका तात्पर्य है कि आपका विलंब फ़ंक्शन पृष्ठभूमि थ्रेड से कोड निष्पादित करता है। आपके उदाहरण का उपयोग करने वाले किसी व्यक्ति के दुर्घटनाग्रस्त होने पर ऐप को डिबग करने में वास्तव में कठिन समय हो सकता है, अगर वे किसी भी यूआई-संबंधित कोड को // देरी कोड कोड के अंदर डालते हैं ।
nalexn

डिफ़ॉल्ट रूप से मेरी विधि मुख्य धागे का उपयोग करती है ताकि ऐसा न हो। प्रेषण देखें। डिफ़ॉल्ट करने के लिए। मुख्य?
जेहुत


4

स्विफ्ट 3 में, हम 'n' सेकंड की देरी के बाद किसी भी फ़ंक्शन या क्रिया को ट्रिगर करने के लिए DispatchQueue.main.asyncAfter फ़ंक्शन का उपयोग कर सकते हैं। यहां कोड में हमने 1 सेकंड के बाद देरी की है। आप इस फ़ंक्शन के शरीर के अंदर किसी भी फ़ंक्शन को कॉल करते हैं जो 1 सेकंड की देरी के बाद ट्रिगर होगा।

let when = DispatchTime.now() + 1
DispatchQueue.main.asyncAfter(deadline: when) {

    // Trigger the function/action after the delay of 1Sec

}

4

Xcode 10.2 और स्विफ्ट 5 और उससे ऊपर

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

1

आप या तो अपनी कक्षा में तर्क को लपेट सकते हैं, या विधि कॉल को ऐसी विधि में लपेट सकते हैं जिसे आदिम प्रकार में पारित करने की आवश्यकता नहीं है। फिर अपने विलंब के बाद उस पद्धति को कॉल करें, और उस पद्धति के भीतर आप जिस चयनकर्ता को प्रदर्शन करना चाहते हैं उसे करें।


1

यहां बताया गया है कि आप स्विफ्ट में देरी के बाद ब्लॉक को कैसे ट्रिगर कर सकते हैं:

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

यह मेरे रेपो में एक मानक कार्य के रूप में शामिल है ।


1

स्विफ्ट 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 :)
        }
    }   
}

0

मेरा मानना ​​है कि लेखक यह नहीं पूछ रहा है कि भिन्नात्मक समय (देरी) का इंतजार कैसे किया जाए, बल्कि चयनकर्ता के तर्क के रूप में एक स्केलर को कैसे पास किया जाए (withObject :) और आधुनिक उद्देश्य C में सबसे तेज़ तरीका है:

[obj performSelector:...  withObject:@(0.123123123) afterDelay:10]

आपके चयनकर्ता को अपने पैरामीटर को NSNumber में बदलना होगा, और फ्लोटवैल्यू या वीवीयू जैसे चयनकर्ता का उपयोग करके मान प्राप्त करना होगा

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.