@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-তে তৈরি করেছে যাতে প্রধান থ্রেডে সুইচ করা স্বয়ংক্রিয় এবং কম্পাইলার-পরীক্ষণযোগ্য হয়, যা রানটাইম ত্রুটির একটি সম্পূর্ণ শ্রেণী দূর করে।
@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 দিয়ে চিহ্নিত হয় এবং অন্য একটি অ্যাসিনক্রোনাস ফাংশন কল করে, এটি ডিফল্টভাবে অ্যাক্টর প্রসঙ্গ উত্তরাধিকার সূত্রে পায়। এর মানে হল যে সমস্ত নেস্টেড কলও প্রধান থ্রেডে নির্বাহিত হয়, যদি না অন্যথায় নির্দিষ্ট করা হয়। কম্পাইলার এটি ট্র্যাক করে এবং একটি অসামঞ্জস্যপূর্ণ ক্লোজার পাস করার চেষ্টা করলে ত্রুটি জারি করে।
@MainActor এবং DispatchQueue.main-এর তুলনা বুঝতে সাহায্য করে কেন নতুন প্রক্রিয়াটি আরও নিরাপদ এবং সুবিধাজনক বলে বিবেচিত হয়, যদিও উভয়ই একই কাজ সমাধান করে — প্রধান থ্রেডে কোড নির্বাহ করা।
@MainActor কম্পাইলার স্তরে একটি পরীক্ষা। যদি আপনি একটি অনিরাপদ প্রসঙ্গ থেকে @MainActor ফাংশন কল করার চেষ্টা করেন, কম্পাইলার একটি সতর্কতা বা ত্রুটি জারি করবে। DispatchQueue.main.async একটি রানটাইম কল: কোড কম্পাইল হবে কিন্তু ব্যাকগ্রাউন্ড থ্রেড থেকে UI আপডেট করার চেষ্টা করলে রানটাইমে ক্র্যাশ হতে পারে।
DispatchQueue.main.async কিউতে একটি ব্লক যোগ করে যা বিলম্বে নির্বাহিত হতে পারে। @MainActor async/await-এর সাথে অপ্রয়োজনীয় ক্লোজার তৈরি না করে সরাসরি নির্বাহক সুইচিং করে। এটি ওভারহেড হ্রাস করে এবং নির্বাহের সময়কে আরও পূর্বাভাসযোগ্য করে।
// পুরানো পদ্ধতি
DispatchQueue.main.async {
self.updateUI()
}
// @MainActor-এর সাথে নতুন পদ্ধতি
@MainActor
func updateUI() {
// প্রধান থ্রেডে নির্বাহিত হয়
self.label.text = "আপডেট করা হয়েছে"
}
| মাপকাঠি | @MainActor | DispatchQueue.main |
|---|---|---|
| পরীক্ষা | কম্পাইলার | রানটাইম |
| সিনট্যাক্স | অ্যানোটেশন (ঘোষণামূলক) | কল (আদেশমূলক) |
| ওভারহেড | কম (নির্বাহক সুইচিং) | মধ্যম (ক্লোজার + কিউ) |
| পরীক্ষণযোগ্যতা | উচ্চ (MainActor.shared প্রতিস্থাপনযোগ্য) | নিম্ন (মক করা কঠিন) |
বাস্তব iOS প্রজেক্টে, @MainActor ViewModel স্তর, SwiftUI ভিউ এবং UIKit কন্ট্রোলারে ব্যবহৃত হয়। অ্যানোটেশনটি পৃথক পদ্ধতি এবং সম্পূর্ণ টাইপ উভয়ের উপর প্রয়োগ করা যেতে পারে।
একটি ক্লাসকে @MainActor দিয়ে চিহ্নিত করে, আপনি নিশ্চিত করেন যে এর সমস্ত পদ্ধতি এবং বৈশিষ্ট্য শুধুমাত্র প্রধান থ্রেডে অ্যাক্সেসযোগ্য। এটি SwiftUI ভিউ এবং ObservableObject ক্লাসের জন্য বিশেষভাবে সুবিধাজনক: আপনি simply 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 দিয়ে চিহ্নিত করার সুপারিশ করা হয়, যখন ব্যবসায়িক যুক্তি এবং নেটওয়ার্ক অনুরোধগুলি ব্যাকগ্রাউন্ড অ্যাক্টর বা গ্লোবাল নির্বাহকের উপর ছেড়ে দেওয়া উচিত।
পুরানো কলব্যাক-ভিত্তিক API (উদাহরণস্বরূপ, async/await ছাড়া URLSession) অ্যাক্টর প্রসঙ্গ সমর্থন করে না। একীকরণের জন্য CheckedContinuation-এর সাথে একটি র্যাপার প্রয়োজন। এছাড়াও, @MainActor performSelector, target-action এবং UIKit-এর অন্যান্য নন-অ্যাসিনক্রোনাস প্যাটার্নের সাথে সামঞ্জস্যপূর্ণ নয়।
@MainActor-এর সাথে অ্যাপ্লিকেশন ডিবাগ করার সময়, রেস কন্ডিশন পুনরুত্পাদন করা কঠিন কারণ কম্পাইলার তাদের অনেকগুলিকে রানটাইমের পরিবর্তে বিল্ড সময়ে প্রতিরোধ করে। তবে, এটি নিরাপত্তার একটি মিথ্যা অনুভূতি তৈরি করতে পারে: শেয়ার্ড মিউটেবল অবজেক্টের (যেমন NSCache বা শেয়ার্ড গ্লোবাল ভেরিয়েবল) সাথে ভুল কাজ এখনও সম্ভব যদি সেগুলি @MainActor দিয়ে চিহ্নিত না হয় এবং স্পষ্ট সিঙ্ক্রোনাইজেশন ছাড়া ব্যবহার করা হয়।
@MainActor UI লজিক পরীক্ষাকে উল্লেখযোগ্যভাবে সহজ করে কারণ এটি পরীক্ষায় ম্যানুয়ালি থ্রেড সুইচ করার প্রয়োজনীয়তা দূর করে। তবে, ইউনিট টেস্ট এবং UI টেস্ট লেখার সময় বিবেচনা করার জন্য বিশেষত্ব রয়েছে।
XCTest-এ, পরীক্ষার পরিবেশ স্বয়ংক্রিয়ভাবে প্রধান থ্রেড নির্বাহক সেট আপ করে। যখন একটি পরীক্ষা পদ্ধতি প্রধান থ্রেডে চলে, @MainActor ফাংশন কল করার জন্য অতিরিক্ত সেটআপের প্রয়োজন হয় না — সেগুলি একই প্রসঙ্গে নির্বাহিত হয়। ব্যাকগ্রাউন্ড পরিস্থিতি পরীক্ষার জন্য, স্পষ্ট অগ্রাধিকার এবং নির্বাহকের সাথে Task-এর ভিতরে MainActor.run ব্যবহার করুন, আলাদাভাবে যাচাই করুন যে কোড ব্যাকগ্রাউন্ড থেকে কল করার সময় সঠিকভাবে কাজ করে।
একটি সাধারণ পদ্ধতি হল @MainActor-এর সাথে ViewModel পরীক্ষা করা, যেখানে যাচাই করা হয় যে অ্যাসিনক্রোনাস অপারেশনের পরে @Published বৈশিষ্ট্যগুলি সঠিকভাবে আপডেট হয়। অ্যাক্টর প্রসঙ্গ উত্তরাধিকারের জন্য ধন্যবাদ, পরীক্ষার ভিতরে await কল করা অতিরিক্ত DispatchQueue গ্যারান্টি বা ম্যানুয়াল প্রসঙ্গ সুইচিং ছাড়াই প্রধান থ্রেডে নির্বাহের নিশ্চয়তা দেয়, যা পরীক্ষা লেখাকে সহজ করে।
বিদ্যমান কোড Swift Concurrency-তে রিফ্যাক্টর করার সময়, কম্পাইলারের মাধ্যমে @MainActor বিচ্ছিন্নতা পরীক্ষা করুন: @MainActor প্রসঙ্গ থেকে @MainActor ছাড়া সিনক্রোনাস পদ্ধতির যেকোনো কল ত্রুটি হিসাবে চিহ্নিত হয়। এই বৈশিষ্ট্যটি একটি প্রকল্পকে ধীরে ধীরে async/await-এ মাইগ্রেট করার জন্য ব্যবহৃত হয়: আপনি ViewModel স্তরকে @MainActor হিসাবে চিহ্নিত করেন, এবং কম্পাইলার সমস্ত অনিরাপদ কল হাইলাইট করে যা ব্যাকগ্রাউন্ড অ্যাক্টরে সরানোর প্রয়োজন।
@MainActor নির্ভরতার জন্য মক তৈরি করার সময়, রিটার্ন টাইপ সহ অ্যাসিনক্রোনাস ফাংশন ঘোষণা করে এমন async পদ্ধতি সহ প্রোটোকল ব্যবহার করুন। এটি অ্যাক্টর বিচ্ছিন্নতা ভাঙ্গা ছাড়াই নেটওয়ার্ক পরিষেবা, ডাটাবেস এবং অন্যান্য বাহ্যিক নির্ভরতা প্রতিস্থাপনের অনুমতি দেয়। কম্পাইলার যাচাই করে যে মক বিচ্ছিন্নতার সমস্ত প্রয়োজনীয়তা প্রয়োগ করে, ব্যাকগ্রাউন্ড পরীক্ষা থ্রেড থেকে @MainActor কোডে আকস্মিক অ্যাক্সেস প্রতিরোধ করে।
@MainActor কোড সিনক্রোনাসভাবে পরীক্ষা করার সময়, অ্যাসিনক্রোনাস অপারেশন সম্পূর্ণ হওয়ার জন্য অপেক্ষা করতে XCTestExpectation ব্যবহার করুন। পরীক্ষায় প্রত্যাশা সেট করুন এবং একটি ক্লোজারের ভিতরে fulfillment কল করুন যা প্রধান থ্রেডে নির্বাহিত হয়। যদি পরীক্ষা অনির্দিষ্টকালের জন্য হ্যাং হয় — সম্ভবত প্রধান থ্রেডে কল হচ্ছে না, এবং আপনার অ্যাক্টর বিচ্ছিন্নতা পরীক্ষা করা প্রয়োজন। নির্বাহ প্রসঙ্গ ডিবাগ করার জন্য, পরীক্ষা কোডের ভিতরে Thread.isMainThread চেক যোগ করা দরকারী।
প্রায়শই জিজ্ঞাসিত প্রশ্ন
না, শুধুমাত্র UI আপডেট করে এমন পদ্ধতিগুলি চিহ্নিত করা যথেষ্ট। তবে, যদি একটি ক্লাসে এই ধরনের বেশ কয়েকটি পদ্ধতি থাকে, তাহলে পুরো ক্লাসে @MainActor যোগ করা সহজ। এটি নিশ্চিত করে যে এর সমস্ত সদস্য প্রধান থ্রেডে নির্বাহিত হয় এবং কোড রক্ষণাবেক্ষণ সহজ করে।
@MainActor প্রধান থ্রেডের সাথে আবদ্ধ একটি গ্লোবাল অ্যাক্টরের একটি নির্দিষ্ট উদাহরণ। @globalActor আপনার নিজস্ব গ্লোবাল অ্যাক্টর তৈরি করার জন্য একটি প্রোটোকল। উদাহরণস্বরূপ, আপনি প্রজেক্ট আর্কিটেকচারের প্রয়োজন হলে ব্যাকগ্রাউন্ড থ্রেডে কোড নির্বাহের জন্য @BackgroundActor তৈরি করতে পারেন।
হ্যাঁ, @MainActor-এর সাথে সিনক্রোনাস ফাংশনও প্রধান থ্রেডে নির্বাহিত হয়। তবে, @MainActor-এর প্রধান মূল্য async/await-এর সাথে প্রকাশিত হয়, যখন একটি অ্যাসিনক্রোনাস ফাংশন DispatchQueue.main-এর মাধ্যমে ম্যানুয়াল সুইচিং ছাড়াই স্বয়ংক্রিয়ভাবে প্রধান থ্রেডে পুনরায় শুরু হয়।
Task.cancel() @MainActor টাস্কের সাথে সাধারণ টাস্কের মতোই কাজ করে। একটি @MainActor টাস্ক Task.isCancelled চেক করতে পারে বা CancellationError থ্রো করতে পারে। বাতিল করার সময়, প্রধান থ্রেড ব্লক হয় না — টাস্কটি নিকটতম সাসপেনশন পয়েন্টে নির্বাহ বন্ধ করে দেয়।
কম্পাইলার নিরাপত্তার নিশ্চয়তা দেয়: যদি আপনি একটি ব্যাকগ্রাউন্ড প্রসঙ্গ থেকে @MainActor ফাংশন কল করেন, কম্পাইলার ত্রুটি নির্দেশ করবে। অ্যাসিনক্রোনাস কলের জন্য, await দিয়ে কলিং কোড চিহ্নিত করা যথেষ্ট, এবং নির্বাহক নিজেই প্রধান থ্রেডে সুইচ করবে। সিনক্রোনাস কলের জন্য, MainActor.run-এর মাধ্যমে স্পষ্ট সুইচিং প্রয়োজন।
সারাংশ
আমরা একটি মোবাইল অ্যাপ্লিকেশন টার্নকি তৈরি করব
IT Sectr 2017 সাল থেকে স্টার্টআপ এবং ব্যবসার জন্য iOS এবং Android অ্যাপ্লিকেশন তৈরি করে। আমরা আপনাকে পরামর্শ দেব এবং সেরা সমাধান প্রস্তাব করব।
আরও পড়ুন