Actor Swift 5.5+ में पेश किया गया एक प्रकार है जो भाषा स्तर पर डेटा रेस की समस्या को हल करता है। मैन्युअल लॉक और DispatchQueue क्यूज़ के विपरीत, actor स्वचालित रूप से अपनी स्थिति को अलग करता है और उस तक पहुँच को सिंक्रोनाइज़ करता है। इसका मतलब है कि दो थ्रेड एक साथ actor प्रकार की एक ही प्रॉपर्टी को संशोधित नहीं कर सकते, जिससे डेवलपर की ओर से अतिरिक्त प्रयास के बिना रेस कंडीशन समाप्त हो जाती है। यह तंत्र actor-आइसोलेशन की अवधारणा पर आधारित है, जहाँ कंपाइलर actor की प्रॉपर्टीज़ और विधियों तक पहुँच को ट्रैक करता है। Apple, 2025 के अनुसार, actor मॉडल Swift की कंकरेंसी प्रणाली का एक मूलभूत हिस्सा है।
मुख्य बिंदु
Actor एक संदर्भ प्रकार है, जो क्लास के समान है, लेकिन डेटा रेस के खिलाफ स्वचालित सुरक्षा के साथ। इसे Swift 5.5 में async/await और Task के साथ कंकरेंसी प्रणाली के हिस्से के रूप में पेश किया गया था। Actor गारंटी देता है कि उसकी परिवर्तनीय स्थिति को बिना स्पष्ट सिंक्रोनाइज़ेशन के विभिन्न थ्रेड्स से एक साथ कभी नहीं पढ़ा या लिखा जाएगा।
एक actor घोषित करने के लिए, actor कीवर्ड का उपयोग किया जाता है जिसके बाद उसके सदस्यों के साथ कर्ली ब्रेसेस आते हैं। सिंटैक्टिक रूप से, actor एक क्लास जैसा दिखता है, लेकिन इसका व्यवहार मौलिक रूप से भिन्न होता है।
actor BankAccount {
private var balance: Double
init(initialBalance: Double) {
self.balance = initialBalance
}
func deposit(amount: Double) {
balance += amount
}
func getBalance() -> Double {
return balance
}
}
कंपाइलर स्वचालित रूप से सभी actor प्रॉपर्टीज़ और विधियों को अलग करता है ताकि वे केवल actor संदर्भ के भीतर ही सुलभ हों। actor के बाहर से balance तक पहुँचने का प्रयास करते समय, कंपाइलर एक त्रुटि उत्पन्न करेगा यदि कॉल को async के रूप में चिह्नित नहीं किया गया है।
डेटा आइसोलेशन actor की मुख्य अवधारणा है। Actor actor निष्पादक तंत्र के माध्यम से अपनी स्थिति तक परस्पर अनन्य पहुँच की गारंटी देता है। प्रत्येक actor का अपना निष्पादक होता है जो उसके पृथक सदस्यों तक सभी पहुँच को क्रमिक रूप से संसाधित करता है।
जब actor के बाहर का कोड उसकी विधि को कॉल करता है, तो कॉल actor निष्पादक क्यू में रखा जाता है। Actor एक समय में केवल एक कार्य करता है, जो रेस की अनुपस्थिति की गारंटी देता है। यदि दो थ्रेड एक साथ deposit कॉल करते हैं, तो दूसरा कॉल पहले के पूरा होने की प्रतीक्षा करता है।
let account = BankAccount(initialBalance: 1000.0)
// एसिंक्रोनस कॉल — actor के बाहर से आवश्यक
await account.deposit(amount: 500.0)
let currentBalance = await account.getBalance()
actor विधि के प्रत्येक कॉल के लिए await की आवश्यकता होती है क्योंकि actor किसी अन्य कार्य में व्यस्त हो सकता है। यह कोई बग नहीं है, बल्कि एक सचेत डिज़ाइन है जो रेस को रोकता है। Swift अंतर्निहित रूप से actor प्रॉपर्टीज़ के गेटर्स और सेटर्स को async बनाता है, इसलिए प्रॉपर्टी पढ़ने के लिए भी await की आवश्यकता होती है।
Actor और क्लास दोनों संदर्भ प्रकार हैं, लेकिन मल्टीथ्रेडेड वातावरण में उनका व्यवहार नाटकीय रूप से भिन्न होता है। क्लास रेस के खिलाफ कोई स्वचालित सुरक्षा प्रदान नहीं करता, जबकि actor इसे टाइप सिस्टम के माध्यम से कंपाइलर स्तर पर शामिल करता है।
| विशेषता | Actor | Class |
|---|---|---|
| रेस सुरक्षा | स्वचालित, कंपाइलर स्तर पर | मैन्युअल सिंक्रोनाइज़ेशन आवश्यक |
| प्रॉपर्टी पहुँच | केवल बाहर से await के माध्यम से | सीधी, बिना सिंक्रोनाइज़ेशन के |
| वंशानुक्रम | केवल अन्य actors से | मानक क्लास-आधारित |
| प्रोटोकॉल अनुरूपता | प्रोटोकॉल का अनुरूप हो सकता है | मानक |
| प्रदर्शन | आइसोलेशन के साथ कम ओवरहेड | सिंक्रोनाइज़ेशन के बिना तेज़ |
एक actor केवल दूसरे actor से वंशानुक्रम प्राप्त कर सकता है और क्लास से वंशानुक्रम नहीं ले सकता। यह जानबूझकर किया गया है क्योंकि क्लास में actor आइसोलेशन तंत्र का अभाव है, और प्रकारों को मिलाने से सुरक्षा गारंटी टूट जाएगी।
actor SavingsAccount: BankAccount {
func applyInterest(rate: Double) {
let interest = balance * rate
balance += interest
}
}
एसिंक्रोनस कॉल्स बाहरी कोड से actor के साथ बातचीत करने का तंत्र हैं। चूँकि actor अपनी स्थिति को अलग करता है, बाहर से उसके सदस्यों तक किसी भी पहुँच के लिए await की आवश्यकता होती है। यह Swift को गारंटी देने की अनुमति देता है कि कॉल करने वाला कोड थ्रेड को ब्लॉक नहीं करता है और actor अन्य अनुरोधों को संसाधित कर सकता है।
घोषित actor प्रकारों के अलावा, Swift ग्लोबल actors का समर्थन करता है — @MainActor विशेषता, जो क्लासेज़, प्रॉपर्टीज़ या विधियों को मुख्य थ्रेड पर निष्पादित होने के रूप में चिह्नित करती है। यह iOS अनुप्रयोगों में UI कोड के लिए विशेष रूप से उपयोगी है।
@MainActor
class ViewModel: ObservableObject {
@Published var title: String = ""
func updateTitle() {
// यह कोड मुख्य थ्रेड पर चलने की गारंटी है
title = "New Title"
}
}
@MainActor का उपयोग मैन्युअल रूप से DispatchQueue.main.async कॉल करने की आवश्यकता को समाप्त करता है, जिससे कोड साफ़ और सुरक्षित हो जाता है। कंपाइलर सुनिश्चित करता है कि मुख्य थ्रेड पर स्विच करना सही ढंग से होता है।
Nonisolated एक कीवर्ड है जो actor की विधि या गणना की गई प्रॉपर्टी को गैर-पृथक के रूप में चिह्नित करने की अनुमति देता है। ऐसे सदस्यों के पास actor-पृथक स्थिति तक पहुँच नहीं है, लेकिन actor के बाहर से await के बिना कॉल किया जा सकता है।
Nonisolated विधियाँ उन गणनाओं के लिए उपयोगी होती हैं जो actor की परिवर्तनीय स्थिति पर निर्भर नहीं करती हैं। उदाहरण के लिए, formatBalance विधि सीधे balance तक नहीं पहुँचती, बल्कि केवल पारित मान को फ़ॉर्मेट करती है — ऐसी विधि को nonisolated बनाना सुरक्षित है।
actor BankAccount {
private var balance: Double = 0
nonisolated func formatBalance(amount: Double) -> String {
return "$\(amount)"
}
}
Nonisolated सदस्य सिंक्रोनस रूप से निष्पादित होते हैं और await की आवश्यकता नहीं होती है। हालाँकि, वे सीधे actor-पृथक प्रॉपर्टीज़ नहीं पढ़ सकते हैं। यदि किसी nonisolated विधि को actor से मान की आवश्यकता है, तो इसे पैरामीटर के रूप में पारित किया जाना चाहिए।
Reentrancy एक तंत्र है जो एसिंक्रोनस कॉल की प्रतीक्षा करते समय actor में पुनः प्रवेश की अनुमति देता है। Reentrancy के बिना, actor हमेशा के लिए डेडलॉक हो सकता है यदि उसकी एक विधि दूसरी की प्रतीक्षा करती है जो बदले में पहली की प्रतीक्षा करती है।
जब actor के अंदर का कोड await निष्पादित करता है, तो actor वर्तमान कार्य को स्थगित करता है और क्यू में रखे किसी अन्य कार्य को संसाधित कर सकता है। await पूरा होने के बाद, कार्य फिर से शुरू होता है। यह डेडलॉक को रोकता है लेकिन सावधानी की आवश्यकता है: await बिंदुओं के बीच actor की स्थिति बदल सकती है।
actor DataProcessor {
var cache: [Int: String] = [:]
func process(id: Int) async -> String {
if let cached = cache[id] {
return cached
}
// await — पुनः प्रवेश बिंदु
let result = await fetchData(id: id)
// await के बाद कैश बदल सकता है — पुनः जाँच करें
cache[id] = result
return result
}
}
डेवलपर्स को reentrancy को ध्यान में रखना चाहिए और await बिंदुओं के बाद actor की स्थिति की जाँच करनी चाहिए। एक सामान्य गलती यह मान लेना है कि actor आइसोलेशन एसिंक्रोनस निलंबन बिंदुओं के माध्यम से बना रहता है। व्यवहार में, await और अगले स्टेटमेंट के बीच, स्थिति अपेक्षा से भिन्न हो सकती है।
अक्सर पूछे जाने वाले प्रश्न
Actor स्वचालित रूप से अपनी स्थिति को डेटा रेस से अलग करता है, बाहरी पहुँच के लिए await की आवश्यकता होती है। क्लास ऐसी सुरक्षा प्रदान नहीं करता — डेवलपर लॉक या क्यू के माध्यम से सिंक्रोनाइज़ेशन के लिए जिम्मेदार है। Actor केवल actor से वंशानुक्रम प्राप्त करता है, क्लास क्लास से।
हाँ, एक actor दूसरे actor से वंशानुक्रम प्राप्त कर सकता है। उपवर्ग को माता-पिता के सभी पृथक गुण और विधियाँ मिलती हैं। Actor क्लास से वंशानुक्रम नहीं ले सकता क्योंकि क्लास में कंपाइलर स्तर पर actor आइसोलेशन तंत्र का अभाव है।
actor-isolated कॉन्टेक्स्ट एक कोड क्षेत्र है जहाँ actor की परिवर्तनीय स्थिति तक सीधी पहुँच की अनुमति है। पृथक के रूप में चिह्नित (डिफ़ॉल्ट रूप से) actor विधियों के अंदर, बिना await के प्रॉपर्टीज़ को पढ़ा और लिखा जा सकता है। कंपाइलर आइसोलेशन सीमाओं की जाँच करता है।
Actor से डेटा async विधियों के माध्यम से भेजा जाता है जो Sendable प्रकार लौटाती हैं, या nonisolated विधियों के माध्यम से जो पैरामीटर के रूप में मान स्वीकार करती हैं। एक async प्रॉपर्टी भी बनाई जा सकती है जो Sendable संरचना के रूप में actor की स्थिति का स्नैपशॉट लौटाती है।
हाँ, actor प्रोटोकॉल का अनुरूप हो सकता है। यदि किसी प्रोटोकॉल में पृथक आवश्यकताएँ (actor-isolated) हैं, तो वे स्वचालित रूप से actor-पृथक हो जाती हैं। प्रोटोकॉल में async विधियों के लिए, यह निर्दिष्ट किया जा सकता है कि उन्हें isolated मार्कर का उपयोग करके एक विशिष्ट actor पर कॉल किया जाना चाहिए।
सारांश
हम एक मोबाइल एप्लिकेशन टर्नकी विकसित करेंगे
IT Sectr 2017 से स्टार्टअप और व्यवसायों के लिए iOS और Android एप्लिकेशन बनाता है। हम आपको सलाह देंगे और सर्वोत्तम समाधान प्रस्तावित करेंगे।
यह भी पढ़ें