Swift में कतारें और बैकग्राउंड टास्क: ऐप को उड़ान भरने दें
\n\nसोचिए: आप एक पिज़्ज़ा ऑर्डर करने वाला ऐप खोलते हैं, "मेनू देखें" पर क्लिक करते हैं, और स्क्रीन पाँच सेकंड के लिए फ्रीज़ हो जाती है। आप परेशान होने लगते हैं, पिज़्ज़ा बेस्वाद लगता है, और ऐप डिलीट करने का मन करता है। यह दर्द जाना-पहचाना है? अगर आप Swift डेवलपर हैं, तो आपने ज़रूर महसूस किया होगा जब इमेज लोडिंग, JSON पार्सिंग या भारी कैलकुलेशन के कारण इंटरफ़ेस "लैग" करता है। स्मूथ UX का राज़ कतारों और बैकग्राउंड टास्क के सही उपयोग में छिपा है।
\n\nइस लेख में हम Swift में मल्टीथ्रेडिंग को व्यवस्थित करना सीखेंगे: क्लासिक GCD और OperationQueue से लेकर आधुनिक async/await तक। आप जानेंगे कि भारी काम को मुख्य थ्रेड से कैसे हटाएँ, डेडलॉक से कैसे बचें, और ऐसा कोड कैसे लिखें जो तेज़ और अनुमानित रूप से काम करे। चलिए शुरू करते हैं!
कतार (Queue) का मतलब सिर्फ लाइन में खड़ा होना नहीं है
\n\nSwift में कतार का मतलब वह जगह नहीं जहाँ आप टोकरी लेकर खड़े होते हैं। कतारें (DispatchQueue) कार्यों को प्रबंधित करने का एक तंत्र हैं। मान लीजिए आपके पास कई रसोइए (थ्रेड) हैं। एक रसोइया हॉल में सेवा करता है (मुख्य थ्रेड — UI), और बाकी किचन में खाना बनाते हैं (बैकग्राउंड थ्रेड)। अगर आप वेटर को स्टेक तलने पर लगा देंगे, तो हॉल ठप हो जाएगा। ठीक यही इंटरफ़ेस के साथ होता है: सभी UI अपडेट केवल मुख्य थ्रेड पर होने चाहिए, बाकी सब कुछ बैकग्राउंड में।
\n\nSwift में दो प्रकार की कतारें हैं:
\n- \n
- Serial (क्रमिक) — कार्य एक के बाद एक होते हैं। साझा संसाधनों की सुरक्षा के लिए आदर्श। \n
- Concurrent (समानांतर) — कार्य एक साथ शुरू होते हैं। इमेज लोड करने या समानांतर गणना के लिए बढ़िया। \n
सिस्टम खुद थ्रेड पूल को मैनेज करता है, इसलिए आपको मैन्युअली थ्रेड बनाने की ज़रूरत नहीं है। बस कहें: "यह बैकग्राउंड में करो, फिर रिज़ल्ट मुख्य थ्रेड पर लाओ।" और Swift इसे बड़ी खूबसूरती से करता है।
\n\nGCD (Grand Central Dispatch) — पुराना दोस्त, नए से कम नहीं
\n\nGrand Central Dispatch एक लो-लेवल API है जो Objective-C के समय से आया है। यह आज भी उपयोगी है, और हर Swift डेवलपर को इसे जानना चाहिए। मुख्य काम DispatchQueue के ज़रिए होता है।
यहाँ एक सबसे सरल उदाहरण है — बैकग्राउंड में डेटा लोड करना और UI अपडेट करना:
\n\nimport UIKit\n\nclass ViewController: UIViewController {\n @IBOutlet weak var imageView: UIImageView!\n \n override func viewDidLoad() {\n super.viewDidLoad()\n loadImage()\n }\n \n func loadImage() {\n // बैकग्राउंड कतार में जाएँ\n DispatchQueue.global(qos: .background).async {\n // भारी काम: इमेज लोड करना\n guard let url = URL(string: "https://example.com/pizza.jpg"),\n let data = try? Data(contentsOf: url),\n let image = UIImage(data: data) else {\n return\n }\n \n // UI अपडेट के लिए मुख्य थ्रेड पर वापस आएँ\n DispatchQueue.main.async {\n self.imageView.image = image\n }\n }\n }\n}\n\n\nqos (Quality of Service) पर ध्यान दें। यह कार्य की प्राथमिकता है: .userInteractive — एनिमेशन के लिए, .background — सिंक्रोनाइज़ेशन के लिए, .utility — प्रोग्रेस इंडिकेटर के लिए। सही QoS चुनने से सिस्टम को संसाधनों का बेहतर वितरण करने में मदद मिलती है।
DispatchGroup: सबका इंतज़ार करें, एक का नहीं
\n\nमान लीजिए आपको तीन इमेज लोड करनी हैं और उसके बाद ही उन्हें दिखाना है। DispatchGroup के बिना आप तीन एसिंक्रोनस रिक्वेस्ट चलाएँगे और पता नहीं चलेगा कि वे कब पूरी होंगी। समाधान — एक ग्रुप:
func loadMultipleImages() {\n let group = DispatchGroup()\n var images: [UIImage] = []\n \n let urls = ["url1", "url2", "url3"]\n \n for urlString in urls {\n group.enter() // ग्रुप में प्रवेश\n DispatchQueue.global().async {\n // लोडिंग