swift - स्विफ्ट में Dispatch_Async का उपयोग करके यूआई को अपडेट करना



(1)

मेरे कोड में मेरे पास लूप के लिए एक सरल है जो एक देरी बनाने के लिए छोरों के लिए नेस्टेड के साथ 100 बार लूप करता है। देरी के बाद, मैं एक डिस्पैच_सुंक के माध्यम से UI में एक प्रगति दृश्य तत्व अपडेट कर रहा हूं। हालाँकि, मुझे अपडेट करने के लिए UI नहीं मिल सकता है। क्या किसी को पता है कि यूआई अपडेट क्यों नहीं कर रहा है? नोट: नीचे दिए गए प्रिंट स्टेटमेंट का उपयोग यह सत्यापित करने के लिए किया जाता है कि लूप के लिए सही ढंग से लूपिंग है।

for i in 0..<100 {

    //Used to create a delay
    for var x = 0; x<100000; x++ {
        for var z = 0; z<1000; z++ {

        }
    }

    println(i)

    dispatch_async(dispatch_get_main_queue()) {
        // update some UI
        self.progressView.setProgress(Float(i), animated: true)

    }
  }

तीन अवलोकन, दो बुनियादी, एक थोड़ा और अधिक उन्नत:

  1. आपका लूप उस मुख्य धागे में यूआई को अपडेट नहीं कर पाएगा जब तक कि लूप खुद दूसरे धागे पर नहीं चल रहा है। तो, आप इसे कुछ पृष्ठभूमि कतार में भेज सकते हैं। स्विफ्ट 3 में:

    DispatchQueue.global(qos: .utility).async {
        for i in 0 ..< kNumberOfIterations {
    
            // do something time consuming here
    
            DispatchQueue.main.async {
                // now update UI on main thread
                self.progressView.setProgress(Float(i) / Float(kNumberOfIterations), animated: true)
            }
        }
    }

    स्विफ्ट 2 में:

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {
        for i in 0 ..< kNumberOfIterations {
    
            // do something time consuming here
    
            dispatch_async(dispatch_get_main_queue()) {
                // now update UI on main thread
                self.progressView.setProgress(Float(i) / Float(kNumberOfIterations), animated: true)
            }
        }
    }
  2. यह भी ध्यान दें कि प्रगति 0.0 से 1.0 तक की संख्या है, इसलिए आप संभवतः लूप के लिए पुनरावृत्तियों की अधिकतम संख्या से विभाजित करना चाहते हैं।

  3. अगर UI अपडेट्स पृष्ठभूमि थ्रेड से अधिक तेज़ी से आते हैं तो UI उन्हें संभाल सकता है, मुख्य थ्रेड अपडेट रिक्वेस्ट के साथ बैकलॉग हो सकता है (यह वास्तव में की तुलना में बहुत धीमा दिखता है)। इसे संबोधित करने के लिए, कोई वास्तविक पृष्ठभूमि अद्यतन प्रक्रिया से "अपडेट UI" कार्य को हटाने के लिए प्रेषण स्रोत का उपयोग करने पर विचार कर सकता है।

    एक DispatchSourceUserDataAdd (स्विफ्ट 2 में, यह DISPATCH_SOURCE_TYPE_DATA_ADD का एक DISPATCH_SOURCE_TYPE_DATA_ADD ) पोस्ट कर सकता है, पोस्ट स्विफ्ट 2 में कॉल ( dispatch_source_merge_data DISPATCH_SOURCE_TYPE_DATA_ADD ) कॉल बैकग्राउंड थ्रेड से जितनी बार वांछित होगी, और यूआई उन्हें जल्दी प्रोसेस करेगा। जब वे data कॉल data (स्विफ्ट 2 में dispatch_source_get_data ) तो वे एक साथ होते हैं यदि पृष्ठभूमि अपडेट UI से अधिक तेज़ी से आते हैं अन्यथा उन्हें संसाधित कर सकते हैं। यह अधिकतम UI अपडेट के साथ अधिकतम पृष्ठभूमि प्रदर्शन को प्राप्त करता है, लेकिन इससे भी महत्वपूर्ण बात यह है कि यह सुनिश्चित करता है कि यूआई एक अड़चन नहीं बनेगा।

    इसलिए, पहले प्रगति का ट्रैक रखने के लिए कुछ चर घोषित करें:

    var progressCounter: UInt = 0

    और अब आपका लूप एक स्रोत बना सकता है, परिभाषित कर सकता है कि स्रोत अपडेट होने पर क्या करना है, और फिर स्रोत को अद्यतन करने वाले अतुल्यकालिक लूप को लॉन्च करें। स्विफ्ट 3 में है कि:

    progressCounter = 0
    
    // create dispatch source that will handle events on main queue
    
    let source = DispatchSource.makeUserDataAddSource(queue: .main)
    
    // tell it what to do when source events take place
    
    source.setEventHandler() { [unowned self] in
        self.progressCounter += source.data
    
        self.progressView.setProgress(Float(self.progressCounter) / Float(kNumberOfIterations), animated: true)
    }
    
    // start the source
    
    source.resume()
    
    // now start loop in the background
    
    DispatchQueue.global(qos: .utility).async {
        for i in 0 ..< kNumberOfIterations {
            // do something time consuming here
    
            // now update the dispatch source
    
            source.add(data: 1)
        }
    }

    स्विफ्ट 2 में:

    progressCounter = 0
    
    // create dispatch source that will handle events on main queue
    
    let source = dispatch_source_create(DISPATCH_SOURCE_TYPE_DATA_ADD, 0, 0, dispatch_get_main_queue());
    
    // tell it what to do when source events take place
    
    dispatch_source_set_event_handler(source) { [unowned self] in
        self.progressCounter += dispatch_source_get_data(source)
    
        self.progressView.setProgress(Float(self.progressCounter) / Float(kNumberOfIterations), animated: true)
    }
    
    // start the source
    
    dispatch_resume(source)
    
    // now start loop in the background
    
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {
        for i in 0 ..< kNumberOfIterations {
    
            // do something time consuming here
    
            // now update the dispatch source
    
            dispatch_source_merge_data(source, 1);
        }
    }




swift