स्विफ्ट 3 जीसीडी एपीआई परिवर्तन के बाद डिस्पैच_ऑन


84

dispatch_onceभाषा संस्करण 3 में किए गए परिवर्तनों के बाद स्विफ्ट के लिए नया वाक्यविन्यास क्या है ? पुराना संस्करण इस प्रकार था।

var token: dispatch_once_t = 0
func test() {
    dispatch_once(&token) {
    }
}

ये उन libdispatch के परिवर्तन हैं जो किए गए थे।



उत्तर stackoverflow.com/a/38311178/1648724 और stackoverflow.com/a/39983813/1648724 के आधार पर , मैंने ऐसा करने के लिए एक कोकोपॉड बनाया: pod 'SwiftDispatchOnce', '~> 1.0'चीयर्स। :]
JRG- डेवलपर

जवाबों:


70

से दस्तावेज़ :

डिस्पैच
नि: शुल्क फ़ंक्शन डिस्पैच_ऑनसेट स्विफ्ट में अब उपलब्ध नहीं है। स्विफ्ट में, आप lazily initialized ग्लोबल्स या स्थिर गुणों का उपयोग कर सकते हैं और एक ही थ्रेड-सेफ्टी प्राप्त कर सकते हैं और एक बार प्रेषण के रूप में गारंटी प्रदान कर सकते हैं। उदाहरण:

let myGlobal: () = { … global contains initialization in a call to a closure … }()
_ = myGlobal  // using myGlobal will invoke the initialization code only the first time it is used.

3
ऐसा नहीं है कि आप नहीं जानते कि स्विफ्ट तेजी से बदल रही है और आपको स्विफ्ट के संस्करणों के बीच बहुत सारे टूटे हुए कोड को सही करना होगा।
अबिज़र्न

2
सबसे बड़ा दर्द 3 पार्टी पॉड्स है जो हमेशा स्विफ्ट 3 संगत नहीं हैं।
Tinkerbell

4
तीसरे पक्ष की निर्भरता, @ टिंकरबेल को पेश करते समय आपके द्वारा अर्जित तकनीकी ऋण। मैं स्विफ्ट से प्यार करता हूं, लेकिन बाहरी निर्भरता में अतिरिक्त सावधानी लाने वाला हूं जो इसे इस कारण से उपयोग करता है।
क्रिस वैगनर

17
dispatch_onceस्पष्ट था। यह, दुर्भाग्य से, बदसूरत और भ्रमित है ..
एलेक्जेंडर जी

104

आलसी इनिशियलाइज़्ड ग्लोबल्स का उपयोग करते समय कुछ एक बार इनिशियलाइज़ेशन के लिए समझ में आता है, यह अन्य प्रकारों के लिए समझ में नहीं आता है। यह बहुत कुछ समझ में आता है कि सिंगलटन जैसी चीजों के लिए आलसी इनिशियलाइज्ड ग्लोबल्स का उपयोग करता है, यह स्विज़ल सेटअप की रखवाली जैसी चीज़ों के लिए बहुत मायने नहीं रखता है।

यहाँ प्रेषण 3 शैली का एक प्रेषण है:

public extension DispatchQueue {

    private static var _onceTracker = [String]()

    /**
     Executes a block of code, associated with a unique token, only once.  The code is thread safe and will
     only execute the code once even in the presence of multithreaded calls.

     - parameter token: A unique reverse DNS style name such as com.vectorform.<name> or a GUID
     - parameter block: Block to execute once
     */
    public class func once(token: String, block:@noescape(Void)->Void) {
        objc_sync_enter(self); defer { objc_sync_exit(self) }

        if _onceTracker.contains(token) {
            return
        }

        _onceTracker.append(token)
        block()
    }
}

यहाँ एक उदाहरण है:

DispatchQueue.once(token: "com.vectorform.test") {
    print( "Do This Once!" )
}

या UUID का उपयोग कर रहा है

private let _onceToken = NSUUID().uuidString

DispatchQueue.once(token: _onceToken) {
    print( "Do This Once!" )
}

जैसा कि हम वर्तमान में स्विफ्ट 2 से 3 के संक्रमण के समय में हैं, यहां एक उदाहरण स्विफ्ट 2 कार्यान्वयन है:

public class Dispatch
{
    private static var _onceTokenTracker = [String]()

    /**
     Executes a block of code, associated with a unique token, only once.  The code is thread safe and will
     only execute the code once even in the presence of multithreaded calls.

     - parameter token: A unique reverse DNS style name such as com.vectorform.<name> or a GUID
     - parameter block: Block to execute once
     */
    public class func once(token token: String, @noescape block:dispatch_block_t) {
        objc_sync_enter(self); defer { objc_sync_exit(self) }

        if _onceTokenTracker.contains(token) {
            return
        }

        _onceTokenTracker.append(token)
        block()
    }

}

समाधान के लिए बहुत-बहुत धन्यवाद। मैं बिल्कुल एक स्विज़ल सेटअप में फंस गया था। मुझे उम्मीद है कि स्विफ्ट टीम इस उपयोग के मामले को संबोधित करेगी।
salman140

2
आप पूरी तरह से objc_sync_enterऔर objc_sync_exitअब का उपयोग नहीं किया जाना चाहिए ।
smat88dd

1
और यही वजह है कि?
टॉड कनिंघम

1
प्रदर्शन के लिए आपको _onceTrackers के लिए एक सरणी के बजाय एक सेट का उपयोग करना चाहिए। यह O (N) से O (1) तक की समय जटिलता में सुधार करता है।
वर्नर अल्टविशचर

2
तो आप एक पुन: प्रयोज्य वर्ग लिखते हैं यह मानते हुए कि इसका पुन: उपयोग नहीं किया जाएगा :-) यदि इसे ओ (एन) से ओ (1) तक समय की जटिलता को कम करने के लिए अतिरिक्त प्रयास की आवश्यकता नहीं है, तो आपको हमेशा इसे आईएमएचओ करना चाहिए।
वर्नर अल्टिवैचर

62

ऊपर टोड कनिंघम के उत्तर पर विस्तार करते हुए, मैंने एक और तरीका जोड़ा है जो फ़ाइल, फ़ंक्शन और लाइन से टोकन को स्वचालित रूप से बनाता है।

public extension DispatchQueue {
    private static var _onceTracker = [String]()

    public class func once(file: String = #file,
                           function: String = #function,
                           line: Int = #line,
                           block: () -> Void) {
        let token = "\(file):\(function):\(line)"
        once(token: token, block: block)
    }

    /**
     Executes a block of code, associated with a unique token, only once.  The code is thread safe and will
     only execute the code once even in the presence of multithreaded calls.

     - parameter token: A unique reverse DNS style name such as com.vectorform.<name> or a GUID
     - parameter block: Block to execute once
     */
    public class func once(token: String,
                           block: () -> Void) {
        objc_sync_enter(self)
        defer { objc_sync_exit(self) }

        guard !_onceTracker.contains(token) else { return }

        _onceTracker.append(token)
        block()
    }
}

तो यह कॉल करने के लिए सरल हो सकता है:

DispatchQueue.once {
    setupUI()
}

और यदि आप चाहें तो आप अभी भी एक टोकन निर्दिष्ट कर सकते हैं:

DispatchQueue.once(token: "com.hostname.project") {
    setupUI()
}

मुझे लगता है कि अगर आप दो मॉड्यूल में एक ही फाइल है तो आप एक टक्कर मिल सकती है। बहुत बुरा नहीं है#module


यह व्हाट्सएप पर कुछ और प्रकाश डालता है। धन्यवाद।
nyxee

उपरोक्त कोड से लिंक करना: gitlab.com/zakkhoyt/DispatchOnceExample/raw/master/…
VaporwareWolf

वास्तव में
थैंक्स की

19

संपादित करें

@ फ्रेज़लाब का उत्तर - यह समाधान धागा-सुरक्षित होने की गारंटी नहीं है। यदि यह महत्वपूर्ण है तो एक विकल्प का उपयोग किया जाना चाहिए

सरल उपाय है

lazy var dispatchOnce : Void  = { // or anyName I choose

    self.title = "Hello Lazy Guy"

    return
}()

जैसे इस्तेमाल किया

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()
    _ = dispatchOnce
}

1
यह बिल्कुल भी मदद नहीं करता है, क्योंकि एक आलसी var घोषणा को नियमित कोड के साथ इनलाइन नहीं किया जा सकता है, इसे एक संरचना या वर्ग परिभाषा में होना चाहिए। इसका मतलब है कि प्रेषण की सामग्री एक उदाहरण के आसपास के दायरे पर कब्जा नहीं कर सकती है। उदाहरण के लिए यदि आप एक बंद घोषित करते हैं जो अभी तक नहीं चला है, तो आप उस बंद के अंदर की संरचना की घोषणा नहीं कर सकते हैं और आलसी संस्करण की सामग्री एक और बंद हो सकती है जो आसपास के बंद होने से var को पकड़ लेती है ...
CommaToast

3
डाउनवोटेड क्योंकि इस कोड में डिस्पैच_ऑन के समान शब्दार्थ नहीं है। dispatch_once यह सुनिश्चित करता है कि कोड को एक बार चलाया जाए, जिस भी थ्रेड से आप इसे कॉल करते हैं । आलसी वार्स का बहु-थ्रेडेड वातावरण में अपरिभाषित व्यवहार होता है।
फ्रिज़लाब Fri ’

इस समाधान में इनिट ब्लॉक कुछ मामलों में दो बार कॉल करने जा रहा है
पवित्र

8

यदि आप ब्रिजिंग हेडर जोड़ते हैं तो भी आप इसका उपयोग कर सकते हैं:

typedef dispatch_once_t mxcl_dispatch_once_t;
void mxcl_dispatch_once(mxcl_dispatch_once_t *predicate, dispatch_block_t block);

तब .mकहीं में:

void mxcl_dispatch_once(mxcl_dispatch_once_t *predicate, dispatch_block_t block) {
    dispatch_once(predicate, block);
}

अब आपको mxcl_dispatch_onceस्विफ्ट से उपयोग करने में सक्षम होना चाहिए ।

अधिकतर आपको Apple के बजाय जो सुझाव देते हैं उनका उपयोग करना चाहिए, लेकिन मेरे पास कुछ वैध उपयोग थे जहां मुझे dispatch_onceदो कार्यों में एक ही टोकन की आवश्यकता थी और इसके बजाय Apple द्वारा प्रदान किए गए द्वारा कवर नहीं किया गया है।


7

आप इस तरह के एक शीर्ष-स्तरीय चर समारोह की घोषणा कर सकते हैं:

private var doOnce: ()->() = {
    /* do some work only once per instance */
    return {}
}()

फिर इसे कहीं भी कॉल करें:

doOnce()

1
आलसी वर्जन को क्लास में स्कोप किया जाता है, इसलिए यह बिल्कुल डिस्पैच_ऑन की तरह काम नहीं करेगा। यह अंतर्निहित वर्ग के उदाहरण के अनुसार एक बार निष्पादित होगा। या तो इसे कक्षा के बाहर ले जाएँ [निजी संस्करण doOnce: () -> () = {}] या इसे चिह्नित करें [स्थिर निजी संस्करण doOnce: () -> () = {}]
एली बर्क

1
बिलकुल सही! धन्यवाद। ज्यादातर मामलों में आपको प्रति बार एक बार कार्रवाई की आवश्यकता होगी।
बोगदान नोविकोव

2
यह एक बहुत अच्छा समाधान है! सुरुचिपूर्ण, लघु और स्पष्ट
बेन लेगियरियो

6

स्विफ्ट 3: उन लोगों के लिए जो पुन: प्रयोज्य वर्गों (या संरचनाओं) को पसंद करते हैं:

public final class /* struct */ DispatchOnce {
   private var lock: OSSpinLock = OS_SPINLOCK_INIT
   private var isInitialized = false
   public /* mutating */ func perform(block: (Void) -> Void) {
      OSSpinLockLock(&lock)
      if !isInitialized {
         block()
         isInitialized = true
      }
      OSSpinLockUnlock(&lock)
   }
}

उपयोग:

class MyViewController: UIViewController {

   private let /* var */ setUpOnce = DispatchOnce()

   override func viewWillAppear() {
      super.viewWillAppear()
      setUpOnce.perform {
         // Do some work here
         // ...
      }
   }

}

अद्यतन (28 अप्रैल 2017): मैकओएस एसडीके 10.12 में कारण पदावनति चेतावनी के OSSpinLockसाथ os_unfair_lock

public final class /* struct */ DispatchOnce {
   private var lock = os_unfair_lock()
   private var isInitialized = false
   public /* mutating */ func perform(block: (Void) -> Void) {
      os_unfair_lock_lock(&lock)
      if !isInitialized {
         block()
         isInitialized = true
      }
      os_unfair_lock_unlock(&lock)
   }
}

मुझे एक संदेश मिलता है कि OSSSpinLock आईओएस 10.0 में पदावनत है
markhorrocks

2
धन्यवाद! उदाहरण कोड अपडेट किया गया। OSSpinLockके साथ बदल दिया os_unfair_lock। BTW: यहाँ एक अच्छा WWDC वीडियो है Concurrent Programming: developer.apple.com/videos/play/wwdc2016/720
व्लाद

0

मुझे जवाब मिलता है कि परिणाम में सुधार होगा:

import Foundation
extension DispatchQueue {
    private static var _onceTracker = [AnyHashable]()

    ///only excute once in same file&&func&&line
    public class func onceInLocation(file: String = #file,
                           function: String = #function,
                           line: Int = #line,
                           block: () -> Void) {
        let token = "\(file):\(function):\(line)"
        once(token: token, block: block)
    }

    ///only excute once in same Variable
    public class func onceInVariable(variable:NSObject, block: () -> Void){
        once(token: variable.rawPointer, block: block)
    }
    /**
     Executes a block of code, associated with a unique token, only once.  The code is thread safe and will
     only execute the code once even in the presence of multithreaded calls.

     - parameter token: A unique reverse DNS style name such as com.vectorform.<name> or a GUID
     - parameter block: Block to execute once
     */
    public class func once(token: AnyHashable,block: () -> Void) {
        objc_sync_enter(self)
        defer { objc_sync_exit(self) }

        guard !_onceTracker.contains(token) else { return }

        _onceTracker.append(token)
        block()
    }

}

extension NSObject {
    public var rawPointer:UnsafeMutableRawPointer? {
        get {
            Unmanaged.passUnretained(self).toOpaque()
        }
    }
}

-3

यदि आप स्विफ्ट 1.2 या इसके बाद के संस्करण का उपयोग कर रहे हैं और नेस्टेड स्ट्रक्चर अप्रोच का उपयोग करें तो यदि आप पहले के संस्करणों का उपयोग करना चाहते हैं, तो क्लास के निरंतर दृष्टिकोण का उपयोग करें। स्विफ्ट में सिंगलटन पैटर्न की खोज। नीचे सभी दृष्टिकोण आलसी आरंभीकरण और थ्रेड सुरक्षा का समर्थन करते हैं। डिस्पैच_ऑनस दृष्टिकोण स्विफ्ट 3.0 में काम नहीं किया गया है

दृष्टिकोण ए: कक्षा स्थिर

class SingletonA {

    static let sharedInstance = SingletonA()

    init() {
        println("AAA");
    }

}

दृष्टिकोण बी: नेस्टेड संरचना

class SingletonB {

    class var sharedInstance: SingletonB {
        struct Static {
            static let instance: SingletonB = SingletonB()
        }
        return Static.instance
    }

}

दृष्टिकोण C: dispatch_once

class SingletonC {

    class var sharedInstance: SingletonC {
        struct Static {
            static var onceToken: dispatch_once_t = 0
            static var instance: SingletonC? = nil
        }
        dispatch_once(&Static.onceToken) {
            Static.instance = SingletonC()
        }
        return Static.instance!
    }
}

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