NotificationCenter হল iOS-এ অ্যাপ্লিকেশন উপাদানগুলির মধ্যে প্রেরক এবং প্রাপকের মধ্যে সরাসরি সংযোগ ছাড়াই বিজ্ঞপ্তি পাঠানো এবং গ্রহণের জন্য একটি সিস্টেম মেকানিজম। Observer প্যাটার্নের উপর ভিত্তি করে, NotificationCenter অবজেক্টগুলিকে ইভেন্টে সাবস্ক্রাইব করতে এবং অ্যাসিঙ্ক্রোনাসভাবে সেগুলিতে প্রতিক্রিয়া জানাতে দেয়। Apple Documentation (2025) অনুযায়ী, NSNotificationCenter post(name:object:) এর মাধ্যমে সিঙ্ক্রোনাস বিজ্ঞপ্তি পাঠানো এবং NotificationQueue-এর মাধ্যমে বিলম্বিত পাঠানো উভয়ই সমর্থন করে। বিজ্ঞপ্তি কেন্দ্র একটি একক প্রক্রিয়ার মধ্যে কাজ করে এবং অ্যাপ্লিকেশন সীমানা অতিক্রম করে না।
মূল পয়েন্ট
NotificationCenter (NSNotificationCenter) অবজেক্টগুলির মধ্যে শিথিলভাবে সংযুক্ত যোগাযোগ বাস্তবায়নের জন্য একটি অন্তর্নির্মিত iOS মেকানিজম। Observer প্যাটার্ন একটি অবজেক্টকে (প্রেরক) তাদের সরাসরি রেফারেন্স ছাড়াই একাধিক অন্যান্য অবজেক্টকে (পর্যবেক্ষক) একটি ইভেন্ট সম্পর্কে জানাতে দেয়। NotificationCenter তিনটি সত্তার সাথে কাজ করে: Notification.Name (বিজ্ঞপ্তি শনাক্তকারী), Notification (ডেটা সহ ধারক) এবং NotificationCenter (ডিসপ্যাচার)। প্রতিটি অ্যাপ্লিকেশনের একটি শেয়ার্ড default center থাকে।
Notification.Name হল একটি কাঠামো যা বিজ্ঞপ্তির ধরন শনাক্ত করে। extension Name: Notification.Name(“MyNotification”) এর মাধ্যমে তৈরি করা হয়। Notification হল একটি অবজেক্ট যাতে name, object (প্রেরক) এবং userInfo (ডেটা সহ অভিধান) থাকে। সিস্টেম বিজ্ঞপ্তিগুলি ধ্রুবক হিসাবে ঘোষণা করা হয়: UIApplication.didBecomeActiveNotification, UIResponder.keyboardWillShowNotification। কাস্টম বিজ্ঞপ্তিগুলি নামের সংঘর্ষ এড়াতে extension এর মাধ্যমে গ্রুপ করা উচিত। নামগুলি রিভার্স-ডোমেন হওয়া উচিত।
// কাস্টম বিজ্ঞপ্তি সংজ্ঞায়িত করা
extension Notification.Name {
static let dataDidUpdate =
Notification.Name("com.app.dataDidUpdate")
static let userLoggedOut =
Notification.Name("com.app.userLoggedOut")
}
// ডেটা সহ একটি বিজ্ঞপ্তি পাঠানো
let userInfo: [String: Any] = [
"userId": 123,
"timestamp": Date()
]
NotificationCenter.default.post(
name: .dataDidUpdate,
object: nil,
userInfo: userInfo
)
একটি পর্যবেক্ষক addObserver(_:selector:name:object:) পদ্ধতির মাধ্যমে একটি বিজ্ঞপ্তিতে সাবস্ক্রাইব করে। Selector হল সেই পদ্ধতি যা বিজ্ঞপ্তি প্রাপ্ত হলে কল করা হবে। object প্যারামিটার একটি নির্দিষ্ট প্রেরকের কাছ থেকে বিজ্ঞপ্তি ফিল্টার করার অনুমতি দেয়। যদি object nil হয়, পর্যবেক্ষক যে কোনো প্রেরকের কাছ থেকে নির্দিষ্ট নামের সমস্ত বিজ্ঞপ্তি গ্রহণ করে। iOS 9 থেকে, addObserver-এর block-based API-এর জন্য ম্যানুয়াল অপসারণের প্রয়োজন হয় না, কিন্তু selector-based-এর এখনও removeObserver প্রয়োজন।
// একটি বিজ্ঞপ্তিতে সাবস্ক্রাইব করা (selector-based)
NotificationCenter.default.addObserver(
self,
selector: #selector(handleDataUpdate),
name: .dataDidUpdate,
object: nil
)
@objc func handleDataUpdate(_ notification: Notification) {
guard let userId = notification.userInfo?["userId"] as? Int else { return }
updateUI(for: userId)
}
// একটি বিজ্ঞপ্তিতে সাবস্ক্রাইব করা (block-based, iOS 9+)
var observer: NSObjectProtocol?
observer = NotificationCenter.default.addObserver(
forName: .dataDidUpdate,
object: nil,
queue: .main
) { [weak self] notification in
guard let self else { return }
self.handleNotification(notification)
}
NotificationCenter একটি ম্যাপিং টেবিল (নাম → পর্যবেক্ষকদের সেট) সংরক্ষণ করে। যখন প্রেরক post(name:object:) কল করে, বিজ্ঞপ্তি কেন্দ্র সিঙ্ক্রোনাসভাবে সেই নামে সাবস্ক্রাইব করা সমস্ত পর্যবেক্ষকের মধ্য দিয়ে যায় এবং তাদের সিলেক্টর বা ব্লক কল করে। মূল বৈশিষ্ট্য: post সমস্ত হ্যান্ডলার সম্পূর্ণ না হওয়া পর্যন্ত বর্তমান থ্রেডকে ব্লক করে। যদি হ্যান্ডলাররা ভারী অপারেশন করে, তবে এটি প্রেরককে বিলম্বিত করে। NotificationQueue বিজ্ঞপ্তি ডেলিভারি বিলম্বিত করে এই সমস্যার সমাধান করে।
post(name:object:userInfo:) পদ্ধতি অবিলম্বে সমস্ত পর্যবেক্ষকের কাছে একটি বিজ্ঞপ্তি পাঠায়। কলটি সিঙ্ক্রোনাস — post-এর পরে কোডটি কেবল সমস্ত হ্যান্ডলার সম্পূর্ণ হওয়ার পরেই কার্যকর হয়। পর্যবেক্ষকদের আহ্বানের ক্রম নিশ্চিত নয় এবং লঞ্চের মধ্যে পরিবর্তিত হতে পারে। অনুক্রমিক প্রক্রিয়াকরণের জন্য, coalescing সহ NotificationQueue ব্যবহার করুন। একই বিজ্ঞপ্তির হ্যান্ডলারের ভিতরে post কল করবেন না — এটি অসীম পুনরাবৃত্তির দিকে পরিচালিত করে।
NotificationQueue অ্যাসিঙ্ক্রোনাস ডেলিভারির জন্য বিজ্ঞপ্তিগুলিকে একটি কিউতে যোগ করে। এটি coalescing (অভিন্ন বিজ্ঞপ্তি একত্রীকরণ) এবং ডেলিভারি কিউ নির্বাচন (asap, idle, modal) সমর্থন করে। Coalescing ঘন ঘন ইভেন্টের জন্য (ডাউনলোড অগ্রগতি) দরকারী যখন আপনাকে শুধুমাত্র সর্বশেষ মান দিয়ে জানাতে হবে। NotificationQueue সক্রিয় হওয়ার জন্য রান লুপ ব্যবহার করে, তাই এটি শুধুমাত্র সক্রিয় রান লুপ সহ থ্রেডে কাজ করে।
// NotificationQueue-এর মাধ্যমে বিলম্বিত পাঠানো
let notification = Notification(
name: .dataDidUpdate,
object: self,
userInfo: ["progress": 0.5]
)
// Coalescing: একাধিক বিজ্ঞপ্তি একটিতে একত্রিত হয়
NotificationQueue.default.enqueue(
notification,
postingStyle: .whenIdle,
coalesceMask: .onName,
forModes: [.common]
)
// DispatchQueue-এর মাধ্যমে অ্যাসিঙ্ক্রোনাস ডেলিভারি
DispatchQueue.main.async {
NotificationCenter.default.post(name: .dataDidUpdate, object: nil)
}
iOS অবজেক্টগুলির মধ্যে যোগাযোগের জন্য তিনটি প্রধান মেকানিজম প্রদান করে: NotificationCenter, Delegate এবং KVO (Key-Value Observing)। প্রতিটি বিজ্ঞপ্তির সমস্যা সমাধান করে কিন্তু সংযুক্তি, কর্মক্ষমতা এবং টাইপ সুরক্ষায় বিভিন্ন আপোষের সাথে। মেকানিজমের পছন্দ এক-থেকে-এক বা এক-থেকে-অনেক সম্পর্ক এবং ডেটা স্থানান্তরের প্রয়োজনীয়তার উপর নির্ভর করে।
| বৈশিষ্ট্য | NotificationCenter | Delegate | KVO |
|---|---|---|---|
| সংযুক্তি | দুর্বল (বিজ্ঞপ্তির নাম) | শক্তিশালী (প্রোটোকল) | মাঝারি (কী) |
| সম্পর্ক | এক-থেকে-অনেক | এক-থেকে-এক | এক-থেকে-অনেক |
| টাইপ সুরক্ষা | নিম্ন (userInfo Dictionary হিসেবে) | উচ্চ (প্রোটোকল পদ্ধতি) | মাঝারি (Any?) |
| কর্মক্ষমতা | মাঝারি (টেবিল অতিক্রম) | উচ্চ (সরাসরি কল) | নিম্ন (NSObject) |
| অ্যাসিঙ্ক্রোনিসিটি | সিঙ্ক্রোনাস (post ব্লক করে) | প্রেরকের থ্রেডে সিঙ্ক্রোনাস | পরিবর্তনে সিঙ্ক্রোনাস |
NotificationCenter ইভেন্টগুলির জন্য আদর্শ যেখানে একাধিক স্বাধীন উপাদানের প্রতিক্রিয়া জানানো উচিত। উদাহরণ: অ্যাপ্লিকেশন সেটিংস পরিবর্তন, ব্যবহারকারী লগআউট, ব্যাকগ্রাউন্ডে পুশ বিজ্ঞপ্তি গ্রহণ করা। NotificationCenter শিথিলভাবে সংযুক্ত মডিউলগুলির জন্যও উপযুক্ত (ফিচার A-এর ফিচার B সম্পর্কে জানা উচিত নয়)। অসুবিধা হল টাইপ সুরক্ষার অভাব: userInfo কীগুলি স্ট্রিং, এনাম নয়।
Delegate স্পষ্ট চুক্তি (tableView.delegate) সহ এক-থেকে-এক সম্পর্কের জন্য পছন্দ। Delegate দ্রুততর এবং টাইপ দ্বারা নিরাপদ। KVO মডেলের একটি নির্দিষ্ট সম্পত্তি (isLoading, progress) পরিবর্তন পর্যবেক্ষণের জন্য পছন্দ। KVO-তে NSObject থেকে উত্তরাধিকার প্রয়োজন এবং ডিবাগিংয়ে অসুবিধা সৃষ্টি করতে পারে (ম্যাজিক স্ট্রিং কী)। আধুনিক Swift-এ, Combine এবং async sequences তিনটি পদ্ধতিই প্রতিস্থাপন করে।
addObserver পদ্ধতি দুটি সাবস্ক্রিপশন বৈকল্পিক সমর্থন করে: selector-based (ঐতিহ্যগত) এবং block-based (ক্লোজার সহ)। Selector-based @objc সামঞ্জস্য এবং ম্যানুয়াল পর্যবেক্ষক অপসারণ প্রয়োজন। Block-based (iOS 9+) একটি ক্যাপচার তালিকা ব্যবহার করার অনুমতি দেয় এবং শক্তিশালী রেফারেন্স ছাড়া ব্লক ব্যবহার করলে OS দ্বারা স্বয়ংক্রিয়ভাবে পরিচালিত হয়। Block-based queue-ও সমর্থন করে — পর্যবেক্ষক নির্দিষ্ট কিউতে বিজ্ঞপ্তি গ্রহণ করে।
সিলেক্টরের মাধ্যমে সাবস্ক্রাইব করার ঐতিহ্যগত উপায়। হ্যান্ডলার পদ্ধতি অবশ্যই @objc দিয়ে চিহ্নিত করা উচিত এবং একটি ঐচ্ছিক Notification গ্রহণ করা উচিত। সুবিধা: লিগ্যাসি Objective-C সহ যেকোনো শ্রেণী দ্বারা ব্যবহার করা যেতে পারে। অসুবিধা: সিলেক্টরের জন্য টাইপ সুরক্ষার অভাব, সিলেক্টর নামে টাইপোর ঝুঁকি, deinit-এ বাধ্যতামূলক removeObserver। যদি পর্যবেক্ষক অবজেক্টের আগে সরানো হয়, হ্যান্ডলার কল করা হবে না।
Block-based API একটি ক্লোজার গ্রহণ করে যা বিজ্ঞপ্তি প্রাপ্ত হলে কার্যকর হয়। queue প্যারামিটার নির্ধারণ করে যে ব্লকটি কোন কিউতে চলে — UI আপডেটের জন্য প্রধান কিউ বা ডেটা প্রক্রিয়াকরণের জন্য ব্যাকগ্রাউন্ড কিউ। রিটার্ন ভ্যালু NSObjectProtocol পর্যবেক্ষক অপসারণ করতে ব্যবহৃত হয়: NotificationCenter.default.removeObserver(observer)। আধুনিক Swift-এ Block-based পছন্দ করা হয়।
protocol NotificationToken {
func dispose()
}
extension NotificationCenter {
func observe(
name: NSNotification.Name,
object: Any? = nil,
queue: OperationQueue? = .main,
using block: @escaping (Notification) -> Void
) -> NotificationToken {
let observer = addObserver(forName: name, object: object,
queue: queue, using: block)
return NotificationTokenWrapper(observer: observer, center: self)
}
}
// স্বয়ংক্রিয় অপসারণ সহ ব্যবহার
class ViewModel {
private var tokens: [NotificationToken] = []
func startObserving() {
let token = NotificationCenter.default.observe(
name: .dataDidUpdate,
queue: .main
) { [weak self] notification in
self?.handleUpdate(notification)
}
tokens.append(token)
}
deinit {
tokens.forEach { $0.dispose() }
}
}
মেমরি লিক NotificationCenter-এর সাথে কাজ করার সময় প্রধান সমস্যাগুলির মধ্যে একটি। যদি ডি-অ্যালোকেশনের আগে পর্যবেক্ষককে সরানো না হয়, তাহলে একটি বিজ্ঞপ্তি পাঠানোর সময় কেন্দ্র ইতিমধ্যে ডি-অ্যালোকেটেড অবজেক্টে একটি পদ্ধতি কল করার চেষ্টা করবে, যার ফলে EXC_BAD_ACCESS হবে। iOS 9 থেকে, block-based addObserver দুর্বল রেফারেন্স ব্যবহার করে, কিন্তু selector-based-এর এখনও ম্যানুয়াল removeObserver প্রয়োজন। সর্বোত্তম অনুশীলন: deinit-এ পর্যবেক্ষক সরান।
Selector-based: সর্বদা deinit-এ NotificationCenter.default.removeObserver(self) কল করুন। যদি পর্যবেক্ষক একাধিক বিজ্ঞপ্তিতে সাবস্ক্রাইব করে, আপনি একবারে সবগুলি (প্যারামিটার ছাড়া) বা নাম দ্বারা নির্দিষ্ট একটি সরাতে পারেন। Block-based: addObserver থেকে প্রাপ্ত টোকেন সহ removeObserver-এর মাধ্যমে সরান। iOS 9+-এ block-based-এর জন্য, লিক হয় না, কিন্তু কর্মক্ষমতার জন্য অপসারণ এখনও সুপারিশ করা হয়: ডি-অ্যালোকেটেড পর্যবেক্ষক post-এর সময় অতিক্রম করা হবে না।
class SafeObserver {
private var observers: [NSObjectProtocol] = []
func addSubscriptions() {
let token1 = NotificationCenter.default.addObserver(
forName: .dataDidUpdate, object: nil,
queue: .main) { [weak self] _ in
self?.refreshData()
}
let token2 = NotificationCenter.default.addObserver(
forName: .userLoggedOut, object: nil,
queue: .main) { [weak self] _ in
self?.logout()
}
observers.append(contentsOf: [token1, token2])
}
deinit {
observers.forEach { NotificationCenter.default.removeObserver($0) }
}
private func refreshData() { }
private func logout() { }
}
টোকেন প্যাটার্ন পর্যবেক্ষক ব্যবস্থাপনা স্বয়ংক্রিয় করে। সাবস্ক্রিপশনে, একটি টোকেন অবজেক্ট (NSObjectProtocol) ফেরত দেওয়া হয় যা ডি-অ্যালোকেশনে স্বয়ংক্রিয়ভাবে পর্যবেক্ষককে সরিয়ে দেয়। NotificationTokenWrapper NotificationCenter এবং পর্যবেক্ষক টোকেনের একটি দুর্বল রেফারেন্স সংরক্ষণ করে, deinit-এ removeObserver কল করে। এটি NotificationCenter-কে Combine পদ্ধতির কাছাকাছি নিয়ে আসে, যেখানে AnyCancellable সাবস্ক্রিপশন লাইফসাইকেল পরিচালনা করে।
থ্রেড সুরক্ষা: NotificationCenter গ্যারান্টি দেয় যে post যেকোনো থ্রেড থেকে কল করা যেতে পারে, এবং সমস্ত পর্যবেক্ষক একই থ্রেডে বিজ্ঞপ্তি পাবে যেখানে post কল করা হয়েছিল। এটি মাল্টিথ্রেডেড অ্যাপ্লিকেশনের জন্য গুরুত্বপূর্ণ: যদি একটি ব্যাকগ্রাউন্ড থ্রেড থেকে বিজ্ঞপ্তি পাঠানো হয়, হ্যান্ডলাররাও ব্যাকগ্রাউন্ড থ্রেডে কার্যকর হবে। UI আপডেটের জন্য, DispatchQueue.main.async-এর মাধ্যমে মূল কিউতে হ্যান্ডলিং প্রেরণ করুন।
NotificationCenter বিভিন্ন থ্রেড থেকে post এবং addObserver কলের জন্য থ্রেড-সুরক্ষিত। অভ্যন্তরীণ সিঙ্ক্রোনাইজেশন লকিং ব্যবহার করে, তাই একাধিক থ্রেড থেকে ঘন ঘন post প্রতিযোগিতা তৈরি করতে পারে। উচ্চ-লোড পরিস্থিতিতে (1000 ফাইলের ডাউনলোড অগ্রগতি), একটি পৃথক বিজ্ঞপ্তি কিউ বা Combine publisher ব্যবহার করুন। postingStyle .now সহ NotificationQueue সরাসরি post-এর সমতুল্য।
NotificationCenter NotificationCenter.default.publisher(for:object:) এর মাধ্যমে Combine publisher সমর্থন করে। Publisher প্রতিটি বিজ্ঞপ্তিকে একটি Combine ইভেন্টে পরিণত করে যা map, filter, debounce এবং throttle-এর মাধ্যমে রূপান্তরিত করা যেতে পারে। এটি সিঙ্ক্রোনাস ডেলিভারি সমস্যার সমাধান করে: Combine নির্দিষ্ট Scheduler-এ বিজ্ঞপ্তিগুলি অ্যাসিঙ্ক্রোনাসভাবে প্রক্রিয়া করে। NotificationCenter.publisher লিগ্যাসি মেকানিজম এবং আধুনিক রিঅ্যাকটিভ প্রোগ্রামিংয়ের মধ্যে একটি সেতু।
import Combine
class ReactiveViewModel {
private var cancellables = Set<AnyCancellable>()
func setupCombineSubscription() {
NotificationCenter.default
.publisher(for: .dataDidUpdate)
.receive(on: DispatchQueue.main)
.compactMap { $0.userInfo?["progress"] as? Float }
.debounce(for: .seconds(0.3), scheduler: RunLoop.main)
.sink { [weak self] progress in
self?.progressLabel.text = "\(Int(progress * 100))%"
}
.store(in: &cancellables)
}
}
সচরাচর জিজ্ঞাসা
হ্যাঁ, NotificationCenter যেকোনো থ্রেড থেকে post এবং addObserver কলের জন্য থ্রেড-সুরক্ষিত। তবে, হ্যান্ডলাররা একই থ্রেডে কার্যকর হয় যেখানে post কল করা হয়েছিল। UI আপডেটের জন্য, block-based addObserver-এ queue: .main ব্যবহার করুন বা হ্যান্ডলারের ভিতরে DispatchQueue.main.async ব্যবহার করুন। receive(on:) সহ Combine publisher থ্রেড সমস্যারও সমাধান করে।
Selector-based: পর্যবেক্ষক ডি-অ্যালোকেশনের পরে বিজ্ঞপ্তি পাঠানোর সময় ক্র্যাশ EXC_BAD_ACCESS। Block-based (iOS 9+): দুর্বল রেফারেন্সের কারণে কোনও লিক নেই, কিন্তু বিজ্ঞপ্তি কেন্দ্র স্পষ্ট removeObserver পর্যন্ত ব্লকটি মেমরিতে ধরে রাখে। সর্বদা deinit-এ পর্যবেক্ষক সরানোর বা স্বয়ংক্রিয় ব্যবস্থাপনার জন্য টোকেন প্যাটার্ন ব্যবহার করার পরামর্শ দেওয়া হয়।
NotificationCenter অসম্পর্কিত উপাদানগুলির মধ্যে নির্বিচারে ইভেন্টের জন্য একটি সম্প্রচার মেকানিজম। KVO একটি নির্দিষ্ট অবজেক্টের একটি নির্দিষ্ট সম্পত্তির পরিবর্তন পর্যবেক্ষণ করে। KVO-তে NSObject উত্তরাধিকার প্রয়োজন এবং setter-এর মাধ্যমে সম্পত্তি পরিবর্তনে স্বয়ংক্রিয়ভাবে জানায়। NotificationCenter শুধুমাত্র যখন post স্পষ্টভাবে কল করা হয় তখনই জানায়। মডেল পর্যবেক্ষণের জন্য, KVO বা Combine পছন্দনীয়।
প্রতি অ্যাপ্লিকেশন প্রক্রিয়ায় একটি default center। NotificationCenter() এর মাধ্যমে অতিরিক্ত কেন্দ্র তৈরি করা যেতে পারে, কিন্তু বাস্তবে শেয়ার্ড default ব্যবহার করা হয়। প্রতিটি কেন্দ্র স্বাধীনভাবে কাজ করে — একটিতে post অন্যটির পর্যবেক্ষকদের কাছে পৌঁছায় না। মডিউল বিচ্ছিন্নতার জন্য, রিভার্স-ডোমেন বিজ্ঞপ্তি নামের মাধ্যমে পৃথক Name নামস্থান ব্যবহার করুন।
আংশিকভাবে। Combine NotificationCenter.Publisher প্রদান করে, যা NotificationCenter-কে একটি রিঅ্যাকটিভ স্ট্রিমে মোড়ানো করে। Combine সিঙ্ক্রোনিসিটি সমস্যা (receive(on:) এর মাধ্যমে) সমাধান করে, রূপান্তর অপারেটর যোগ করে এবং স্বয়ংক্রিয় সাবস্ক্রিপশন ব্যবস্থাপনা (AnyCancellable) করে। তবে, iOS সিস্টেম বিজ্ঞপ্তি (UIApplication, UIKeyboard) এবং লিগ্যাসি কোডের জন্য NotificationCenter রয়ে গেছে। Combine একটি উন্নতি, প্রতিস্থাপন নয়।
সারাংশ
আমরা একটি মোবাইল অ্যাপ্লিকেশন টার্নকি তৈরি করব
IT Sectr 2017 সাল থেকে স্টার্টআপ এবং ব্যবসার জন্য iOS এবং Android অ্যাপ্লিকেশন তৈরি করে। আমরা আপনাকে পরামর্শ দেব এবং সেরা সমাধান প্রস্তাব করব।
আরও পড়ুন