@MainActor Swift زبان میں ایک عالمی اداکار (گلوبل ایکٹر) ہے جو مرکزی تھریڈ پر کوڈ کے اجراء کی ضمانت دیتا ہے۔ Apple Developer, 2024 کے مطابق، @MainActor UI کے ساتھ کام کرتے ہوئے مرکزی تھریڈ پر سوئچ کرنے کو خودکار بناتا ہے، ڈیولپر کو دستی طور پر DispatchQueue.main.async کال کرنے سے آزاد کرتا ہے۔ یہ تشریح Swift 5.5 میں async/await نظام کے ساتھ ظاہر ہوئی۔
اہم نکات
@MainActor Swift میں ایک عالمی اداکار ہے جو اداکاروں کی خصوصیات کو ایپلیکیشن کے مرکزی تھریڈ پر اجراء کی ضمانت کے ساتھ جوڑتا ہے۔ یہ Swift 5.5 میں async/await اور ساختی ہم وقتی کے ساتھ متعارف کرائے گئے Swift ہم وقتی نظام کا حصہ ہے۔ تشریح ڈیولپر کو دستی تھریڈ سوئچنگ کے بارے میں سوچنے کی ضرورت نہیں دیتی اور UI کی غلطیوں کی تعداد کو کم کرتی ہے۔
Swift میں اداکار ایک حوالہ کی قسم ہے جو اپنی حالت کو الگ کرتا ہے اور ضمانت دیتا ہے کہ صرف ایک تھریڈ اسے تبدیل کر سکتا ہے۔ @MainActor ایک خاص عالمی اداکار ہے جس کا عمل دہندہ مرکزی تھریڈ ہے۔ @MainActor سے نشان زد کوئی بھی کوڈ مرکزی تھریڈ پر عمل کرتا ہے — چاہے وہ پس منظر کے کام سے بلایا گیا ہو۔
@MainActor سے پہلے، ڈیولپر DispatchQueue.main.async کے ذریعے دستی طور پر مرکزی تھریڈ پر سوئچ کرتے تھے۔ یہ بار بار غلطیوں کا ذریعہ تھا: ڈیولپر سوئچ کرنا بھول جاتے تھے، جس کے نتیجے میں مرکزی تھریڈ پر UI اپ ڈیٹ نہ ہونے کی وجہ سے کریش ہوتے تھے۔ @MainActor اس مسئلے کو قسم کے نظام کی سطح پر حل کرتا ہے۔
iOS ایپلیکیشنز میں زیادہ تر بگز کا ذریعہ UI کی عدم تحفظ ہے — پس منظر کے تھریڈ سے انٹرفیس کو اپ ڈیٹ کرنا۔ Apple نے @MainActor کو Swift Concurrency میں شامل کیا تاکہ مرکزی تھریڈ پر سوئچ کرنا خودکار اور مرتب کنندہ کے ذریعے قابل جانچ ہو، جس سے runtime کی غلطیوں کی ایک پوری کلاس ختم ہو جائے۔
@MainActor کے کام کرنے کا اصول Swift Concurrency کے اجراء کے نظام پر مبنی ہے۔ جب کوئی تھریڈ @MainActor سے نشان زد فنکشن کو کال کرتا ہے، تو شیڈیولر اسے موجودہ عمل دہندہ پر معطل کرتا ہے اور مرکزی تھریڈ پر دوبارہ شروع کرتا ہے۔ مرتب کنندہ کال کی حدود کو ٹریک کرتا ہے اور تحفظ کی ضمانت دیتا ہے۔
@MainActor کا اجراء MainActor.shared کے ذریعے منظم کیا جاتا ہے — ایک عمل دہندہ جو ایپلیکیشن کے مرکزی تھریڈ سے منسلک ہوتا ہے۔ جب ایک غیر متزامن فنکشن @MainActor سے نشان زد ہوتا ہے، تو یہ ہمیشہ اس عمل دہندہ پر دوبارہ شروع ہوتا ہے، قطع نظر اس کے کہ اصل کام کس تھریڈ پر شروع کیا گیا تھا۔
import SwiftUI
class ViewModel: ObservableObject {
@Published var items: [String] = []
@MainActor
func loadData() async {
let result = await fetchRemoteData()
items = result // محفوظ طریقے سے، MainActor مرکزی تھریڈ کی ضمانت دیتا ہے
}
}
اگر کوئی فنکشن @MainActor سے نشان زد ہے اور کسی دوسرے غیر متزامن فنکشن کو کال کرتا ہے، تو یہ ڈیفالٹ طور پر اداکار کے سیاق و سباق کو وراثت میں لیتا ہے۔ اس کا مطلب ہے کہ تمام嵌套 کالز بھی مرکزی تھریڈ پر عمل کرتی ہیں، جب تک کہ دوسری صورت میں وضاحت نہ کی گئی ہو۔ مرتب کنندہ اسے ٹریک کرتا ہے اور غیر مطابقت پذیر بندش (closure) منتقل کرنے کی کوشش پر غلطی جاری کرتا ہے۔
@MainActor اور DispatchQueue.main کا موازنہ یہ سمجھنے میں مدد کرتا ہے کہ نیا طریقہ کار کیوں زیادہ محفوظ اور آسان سمجھا جاتا ہے، اگرچہ دونوں ایک ہی کام حل کرتے ہیں — مرکزی تھریڈ پر کوڈ عمل کرنا۔
@MainActor مرتب کنندہ کی سطح پر جانچ ہے۔ اگر آپ غیر محفوظ سیاق و سباق سے @MainActor فنکشن کو کال کرنے کی کوشش کرتے ہیں، تو مرتب کنندہ انتباہ یا غلطی جاری کرے گا۔ DispatchQueue.main.async ایک runtime کال ہے: کوڈ مرتب ہو جائے گا لیکن پس منظر کے تھریڈ سے UI اپ ڈیٹ کرنے کی کوشش پر runtime میں کریش ہو سکتا ہے۔
DispatchQueue.main.async قطار میں ایک بلاک شامل کرتا ہے جو تاخیر سے عمل ہو سکتا ہے۔ @MainActor async/await کے ساتھ غیر ضروری بندشیں پیدا کیے بغیر براہ راست عمل دہندہ سوئچنگ کرتا ہے۔ یہ اضافی بوجھ کو کم کرتا ہے اور عملدرآمد کے وقت کو زیادہ پیش قیاس بناتا ہے۔
// پرانا طریقہ
DispatchQueue.main.async {
self.updateUI()
}
// @MainActor کے ساتھ نیا طریقہ
@MainActor
func updateUI() {
// مرکزی تھریڈ پر عمل کرتا ہے
self.label.text = "اپ ڈیٹ کیا گیا"
}
| معیار | @MainActor | DispatchQueue.main |
|---|---|---|
| جانچ | مرتب کنندہ | runtime |
| نحوی ساخت | تشریح (اعلامی) | کال (حکمی) |
| اضافی بوجھ | کم (عمل دہندہ سوئچنگ) | درمیانی (بندش + قطار) |
| جانچ پڑتال کی صلاحیت | اعلی (MainActor.shared تبدیل کیا جا سکتا ہے) | کم (نقل کرنا مشکل) |
حقیقی iOS پروجیکٹس میں، @MainActor ViewModel کی تہوں، SwiftUI ویوز اور UIKit کنٹرولرز میں استعمال ہوتا ہے۔ تشریح انفرادی طریقوں اور پوری قسم دونوں پر لاگو کی جا سکتی ہے۔
کسی کلاس کو @MainActor سے نشان زد کرکے، آپ ضمانت دیتے ہیں کہ اس کے تمام طریقے اور خصوصیات صرف مرکزی تھریڈ پر قابل رسائی ہیں۔ یہ SwiftUI ویوز اور ObservableObject کلاسز کے لیے خاص طور پر آسان ہے: آپ صرف class سے پہلے @MainActor شامل کرتے ہیں، اور تمام @Published خصوصیات محفوظ طریقے سے اپ ڈیٹ ہوتی ہیں۔
@MainActor
final class UserListViewModel: ObservableObject {
@Published var users: [User] = []
@Published var isLoading = false
func fetchUsers() async {
isLoading = true
users = await api.getUsers()
isLoading = false
}
}
پرانی UIKit کوڈ کے ساتھ کام کرتے وقت جہاں تھریڈ سوئچنگ دستی تھی، آپ واضح سوئچنگ کے لیے MainActor.run استعمال کر سکتے ہیں۔ یہ پورے کوڈبیس کو دوبارہ لکھے بغیر Swift Concurrency میں بتدریج منتقلی کے لیے آسان ہے۔
await MainActor.run {
self.tableView.reloadData()
}
اپنے تمام فوائد کے باوجود، @MainActor کی کئی حدود ہیں جن پر ایپلیکیشن آرکیٹیکچر ڈیزائن کرتے وقت غور کرنا ضروری ہے۔ اطلاق کی حدود کو سمجھنا غلط استعمال سے بچنے میں مدد کرتا ہے۔
اگر کال کا پورا سلسلہ @MainActor سے نشان زد ہے، تو کوئی بھی بھاری کام مرکزی تھریڈ پر انجام پائے گا، جس سے UI منجمد ہو جائے گا۔ صرف UI کی تہہ کو @MainActor سے نشان زد کرنے کی سفارش کی جاتی ہے، جبکہ کاروباری منطق اور نیٹ ورک کی درخواستوں کو پس منظر کے اداکاروں یا عالمی عمل دہندہ پر چھوڑ دینا چاہیے۔
پرانی callback-based API (مثال کے طور پر، async/await کے بغیر URLSession) اداکار کے سیاق و سباق کی حمایت نہیں کرتیں۔ انضمام کے لیے CheckedContinuation کے ساتھ ایک ریپر کی ضرورت ہوتی ہے۔ اس کے علاوہ، @MainActor performSelector، target-action اور UIKit کے دیگر غیر غیر متزامن نمونوں کے ساتھ مطابقت نہیں رکھتا۔
@MainActor کے ساتھ ایپلیکیشنز کو ڈیبگ کرتے وقت، ریس کنڈیشنز کو دوبارہ پیدا کرنا مشکل ہے کیونکہ مرتب کنندہ ان میں سے بہت سے کو runtime کے بجائے بلڈ ٹائم پر روکتا ہے۔ تاہم، یہ تحفظ کا غلط احساس پیدا کر سکتا ہے: مشترکہ تبدیل ہونے والی اشیاء (جیسے NSCache یا مشترکہ عالمی متغیرات) کے ساتھ غلط کام اب بھی ممکن ہے اگر وہ @MainActor سے نشان زد نہ ہوں اور واضح ہم آہنگی کے بغیر استعمال ہوں۔
@MainActor UI منطق کی جانچ کو نمایاں طور پر آسان بناتا ہے کیونکہ یہ جانچوں میں دستی تھریڈ سوئچنگ کی ضرورت کو ختم کرتا ہے۔ تاہم، یونٹ ٹیسٹ اور UI ٹیسٹ لکھتے وقت جن خصوصیات پر غور کرنا ہے وہ موجود ہیں۔
XCTest میں، جانچ کا ماحول خود بخود مرکزی تھریڈ کے عمل دہندہ کو ترتیب دیتا ہے۔ جب کوئی جانچ کا طریقہ مرکزی تھریڈ پر چلتا ہے، @MainActor فنکشنز کو کال کرنے کے لیے اضافی سیٹ اپ کی ضرورت نہیں ہوتی — وہ اسی سیاق و سباق میں عمل کرتے ہیں۔ پس منظر کے منظرناموں کی جانچ کے لیے، واضح ترجیح اور عمل دہندہ کے ساتھ Task کے اندر MainActor.run استعمال کریں، اور علیحدہ سے تصدیق کریں کہ کوڈ پس منظر سے بلائے جانے پر صحیح کام کرتا ہے۔
ایک عام طریقہ @MainActor کے ساتھ ViewModel کی جانچ ہے، جہاں تصدیق کی جاتی ہے کہ غیر متزامن کارروائیوں کے بعد @Published خصوصیات صحیح طور پر اپ ڈیٹ ہوتی ہیں۔ اداکار کے سیاق و سباق کی وراثت کی بدولت، جانچ کے اندر await کو کال کرنا اضافی DispatchQueue ضمانتوں یا دستی سیاق و سباق کی سوئچنگ کے بغیر مرکزی تھریڈ پر عملدرآمد کی ضمانت دیتا ہے، جو جانچ لکھنے کو آسان بناتا ہے۔
موجودہ کوڈ کو Swift Concurrency میں ری فیکٹر کرتے وقت، مرتب کنندہ کے ذریعے @MainActor کی علیحدگی کی جانچ کریں: @MainActor سیاق و سباق سے @MainActor کے بغیر sync طریقوں کی کوئی بھی کال غلطی کے طور پر نشان زد ہوتی ہے۔ یہ خاصیت کسی پروجیکٹ کو بتدریج async/await میں منتقل کرنے کے لیے استعمال ہوتی ہے: آپ ViewModel کی تہہ کو @MainActor کے طور پر نشان زد کرتے ہیں، اور مرتب کنندہ تمام غیر محفوظ کالوں کو نمایاں کرتا ہے جنہیں پس منظر کے اداکاروں میں منتقل کرنے کی ضرورت ہے۔
@MainActor انحصار کے لیے نقلیں بناتے وقت، واپسی کی اقسام کے ساتھ غیر متزامن فنکشنز کا اعلان کرنے والے async طریقوں والے پروٹوکول استعمال کریں۔ یہ اداکار کی علیحدگی کو توڑے بغیر نیٹ ورک سروسز، ڈیٹا بیسز اور دیگر بیرونی انحصاروں کو تبدیل کرنے کی اجازت دیتا ہے۔ مرتب کنندہ تصدیق کرتا ہے کہ نقل علیحدگی کی تمام ضروریات کو نافذ کرتی ہے، پس منظر کے جانچ کے تھریڈز سے @MainActor کوڈ تک حادثاتی رسائی کو روکتی ہے۔
@MainActor کوڈ کو sync طریقے سے جانچتے وقت، غیر متزامن کارروائیوں کی تکمیل کے انتظار کے لیے XCTestExpectation استعمال کریں۔ جانچ میں توقع مقرر کریں اور ایک بندش کے اندر fulfillment کو کال کریں جو مرکزی تھریڈ پر عمل کرتی ہے۔ اگر جانچ غیر معینہ طور پر پھنس جائے — تو ممکنہ طور پر مرکزی تھریڈ پر کال نہیں ہو رہی، اور آپ کو اداکار کی علیحدگی جانچنے کی ضرورت ہے۔ عملدرآمد کے سیاق و سباق کو ڈیبگ کرنے کے لیے، جانچ کے کوڈ کے اندر Thread.isMainThread چیک شامل کرنا مفید ہے۔
اکثر پوچھے گئے سوالات
نہیں، صرف ان طریقوں کو نشان زد کرنا کافی ہے جو UI کو اپ ڈیٹ کرتے ہیں۔ تاہم، اگر کسی کلاس میں ایسے کئی طریقے ہیں، تو پوری کلاس میں @MainActor شامل کرنا آسان ہے۔ یہ ضمانت دیتا ہے کہ اس کے تمام ارکان مرکزی تھریڈ پر عمل کرتے ہیں اور کوڈ کی دیکھ بھال کو آسان بناتا ہے۔
@MainActor مرکزی تھریڈ سے منسلک عالمی اداکار کی ایک مخصوص مثال ہے۔ @globalActor آپ کے اپنے عالمی اداکار بنانے کے لیے ایک پروٹوکول ہے۔ مثال کے طور پر، اگر پروجیکٹ آرکیٹیکچر کی ضرورت ہو تو آپ پس منظر کے تھریڈ پر کوڈ عمل کرنے کے لیے @BackgroundActor بنا سکتے ہیں۔
ہاں، @MainActor کے ساتھ sync فنکشنز بھی مرکزی تھریڈ پر عمل کرتے ہیں۔ تاہم، @MainActor کی اصل قدر async/await کے ساتھ ظاہر ہوتی ہے، جب ایک غیر متزامن فنکشن DispatchQueue.main کے ذریعے دستی سوئچنگ کے بغیر خود بخود مرکزی تھریڈ پر دوبارہ شروع ہوتا ہے۔
Task.cancel() @MainActor کاموں کے ساتھ عام کاموں کی طرح کام کرتا ہے۔ ایک @MainActor کام Task.isCancelled کو چیک کر سکتا ہے یا CancellationError پھینک سکتا ہے۔ منسوخی پر، مرکزی تھریڈ بلاک نہیں ہوتا — کام قریب ترین معطلی کے مقام پر عملدرآمد روک دیتا ہے۔
مرتب کنندہ تحفظ کی ضمانت دیتا ہے: اگر آپ پس منظر کے سیاق و سباق سے @MainActor فنکشن کو کال کرتے ہیں، تو مرتب کنندہ غلطی کی نشاندہی کرے گا۔ غیر متزامن کالز کے لیے، کال کرنے والے کوڈ کو await سے نشان زد کرنا کافی ہے، اور عمل دہندہ خود بخود مرکزی تھریڈ پر سوئچ ہو جائے گا۔ sync کالز کے لیے، MainActor.run کے ذریعے واضح سوئچنگ کی ضرورت ہے۔
خلاصہ
ہم ایک موبائل ایپلیکیشن ٹرنکی تیار کریں گے
IT Sectr 2017 سے اسٹارٹ اپس اور کاروبار کے لیے iOS اور Android ایپلیکیشنز بناتا ہے۔ ہم آپ کو مشورہ دیں گے اور بہترین حل تجویز کریں گے۔
مزید پڑھیں