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 αποθηκεύει έναν πίνακα αντιστοίχισης (name → σύνολο παρατηρητών). Όταν ο αποστολέας καλεί post(name:object:), το κέντρο ειδοποιήσεων διατρέχει σύγχρονα όλους τους παρατηρητές που είναι εγγεγραμμένοι σε αυτό το όνομα και καλεί τους selectors ή τα blocks τους. Βασικό χαρακτηριστικό: το post μπλοκάρει το τρέχον νήμα μέχρι την ολοκλήρωση όλων των χειριστών. Αν οι χειριστές εκτελούν βαριές λειτουργίες, αυτό καθυστερεί τον αποστολέα. Το NotificationQueue λύνει αυτό το πρόβλημα καθυστερώντας την παράδοση ειδοποιήσεων.
Η μέθοδος post(name:object:userInfo:) στέλνει την ειδοποίηση αμέσως σε όλους τους παρατηρητές. Η κλήση είναι σύγχρονη — ο κώδικας μετά το post εκτελείται μόνο μετά την ολοκλήρωση όλων των χειριστών. Η σειρά κλήσης των παρατηρητών δεν είναι εγγυημένη και μπορεί να αλλάζει μεταξύ εκτελέσεων. Για σειριακή επεξεργασία χρησιμοποιήστε το NotificationQueue με coalescing. Μην καλείτε το post μέσα σε χειριστή της ίδιας ειδοποίησης — αυτό οδηγεί σε ατέρμονη αναδρομή.
NotificationQueue προσθέτει ειδοποιήσεις σε ουρά για ασύγχρονη παράδοση. Υποστηρίζει coalescing (συγχώνευση πανομοιότυπων ειδοποιήσεων) και επιλογή ουράς παράδοσης (asap, idle, modal). Το coalescing είναι χρήσιμο για συχνά συμβάντα (πρόοδος φόρτωσης), όταν χρειάζεται να ειδοποιήσετε μόνο με την τελευταία τιμή. Το NotificationQueue χρησιμοποιεί run loop για ενεργοποίηση, επομένως λειτουργεί μόνο σε νήματα με ενεργό run loop.
// Καθυστερημένη αποστολή μέσω 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 είναι ιδανικό για συμβάντα στα οποία πρέπει να αντιδράσουν πολλά ανεξάρτητα στοιχεία. Παραδείγματα: αλλαγή ρυθμίσεων εφαρμογής, αποσύνδεση χρήστη, λήψη push ειδοποίησης στο παρασκήνιο. Το NotificationCenter είναι επίσης κατάλληλο για χαλαρά συνδεδεμένες μονάδες (η λειτουργία Α δεν χρειάζεται να γνωρίζει για τη λειτουργία Β). Μειονέκτημα — δεν υπάρχει ασφάλεια τύπων: τα κλειδιά userInfo είναι συμβολοσειρές, όχι enum.
Delegate επιλέξτε για σχέση ένας-προς-έναν με σαφή σύμβαση (tableView.delegate). Ο Delegate είναι ταχύτερος και ασφαλέστερος ως προς τους τύπους. KVO επιλέξτε για παρακολούθηση αλλαγής συγκεκριμένης ιδιότητας μοντέλου (isLoading, progress). Το KVO απαιτεί κληρονομικότητα από NSObject και μπορεί να προκαλέσει δυσκολίες στον εντοπισμό σφαλμάτων (μαγικές συμβολοσειρές κλειδιών). Στη σύγχρονη Swift, τα Combine και async sequences αντικαθιστούν και τις τρεις προσεγγίσεις.
Η μέθοδος addObserver υποστηρίζει δύο παραλλαγές εγγραφής: selector-based (παραδοσιακή) και block-based (με closure). Selector-based απαιτεί συμβατότητα @objc και χειροκίνητη διαγραφή του παρατηρητή. Block-based (iOS 9+) επιτρέπει τη χρήση capture list και διαχειρίζεται αυτόματα από το OS όταν χρησιμοποιούνται blocks χωρίς ισχυρές αναφορές. Το block-based υποστηρίζει επίσης queue — ο παρατηρητής λαμβάνει την ειδοποίηση στην καθορισμένη ουρά.
Παραδοσιακός τρόπος εγγραφής μέσω selector. Η μέθοδος χειριστή πρέπει να επισημαίνεται με @objc και να δέχεται ένα προαιρετικό Notification. Πλεονέκτημα: δυνατότητα χρήσης από οποιαδήποτε κλάση, συμπεριλαμβανομένου του legacy Objective-C. Μειονεκτήματα: έλλειψη ασφάλειας τύπων selector, κίνδυνος τυπογραφικών σφαλμάτων στο όνομα selector, υποχρεωτικό removeObserver στο deinit. Αν ο παρατηρητής αφαιρεθεί πριν από το αντικείμενο, ο χειριστής δεν θα κληθεί.
Το block-based API δέχεται ένα closure που εκτελείται κατά τη λήψη της ειδοποίησης. Παράμετρος queue καθορίζει σε ποια ουρά εκτελείται το block — main queue για ενημερώσεις UI ή background queue για επεξεργασία δεδομένων. Η επιστρεφόμενη τιμή NSObjectProtocol χρησιμοποιείται για τη διαγραφή του παρατηρητή: NotificationCenter.default.removeObserver(observer). Το block-based προτιμάται στη σύγχρονη Swift.
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. Best practice: διαγράψτε τον παρατηρητή στο deinit.
Selector-based: καλέστε υποχρεωτικά το NotificationCenter.default.removeObserver(self) στο deinit. Αν ο παρατηρητής είναι εγγεγραμμένος σε πολλές ειδοποιήσεις, μπορείτε να τις διαγράψετε όλες ταυτόχρονα (χωρίς παραμέτρους) ή μια συγκεκριμένη με όνομα. Block-based: διαγράψτε μέσω removeObserver με το token που λάβατε από το addObserver. Για block-based σε iOS 9+ δεν συμβαίνει διαρροή, αλλά η διαγραφή εξακολουθεί να συνιστάται για απόδοση: οι ελευθερωμένοι παρατηρητές δεν θα διατρέχονται κατά το 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() { }
}
Το πρότυπο Token αυτοματοποιεί τη διαχείριση παρατηρητών. Κατά την εγγραφή, επιστρέφεται ένα αντικείμενο-token (NSObjectProtocol), το οποίο κατά την αποδέσμευση διαγράφει αυτόματα τον παρατηρητή. NotificationTokenWrapper αποθηκεύει αδύναμη αναφορά στο NotificationCenter και στο token παρατηρητή, καλώντας το removeObserver στο deinit. Αυτό φέρνει το NotificationCenter πιο κοντά στην προσέγγιση Combine, όπου το AnyCancellable διαχειρίζεται τον κύκλο ζωής της εγγραφής.
Thread safety Το NotificationCenter εγγυάται ότι το post μπορεί να κληθεί από οποιοδήποτε νήμα και όλοι οι παρατηρητές θα λάβουν την ειδοποίηση στο ίδιο νήμα όπου κλήθηκε το post. Αυτό είναι κρίσιμο για πολυνηματικές εφαρμογές: αν η ειδοποίηση στάλθηκε από νήμα παρασκηνίου, οι χειριστές θα εκτελεστούν επίσης στο νήμα παρασκηνίου. Για ενημέρωση UI, η επεξεργασία πρέπει να δρομολογηθεί στο main queue μέσω DispatchQueue.main.async.
Το NotificationCenter είναι ασφαλές για νήματα για κλήσεις post και addObserver από διαφορετικά νήματα. Εσωτερικός συγχρονισμός χρησιμοποιεί κλείδωμα, επομένως συχνά post από πολλά νήματα μπορεί να δημιουργήσουν contention. Για σενάρια υψηλού φορτίου (πρόοδος φόρτωσης 1000 αρχείων) χρησιμοποιήστε ξεχωριστή ουρά ειδοποιήσεων ή Combine publisher. Το NotificationQueue με postingStyle .now είναι ισοδύναμο με άμεσο post.
Το NotificationCenter υποστηρίζει Combine publisher μέσω NotificationCenter.default.publisher(for:object:). Publisher μετατρέπει κάθε ειδοποίηση σε συμβάν Combine που μπορεί να μετασχηματιστεί μέσω map, filter, debounce και throttle. Αυτό λύνει το πρόβλημα της σύγχρονης παράδοσης: το Combine επεξεργάζεται ειδοποιήσεις ασύγχρονα στο καθορισμένο Scheduler. Το NotificationCenter.publisher — είναι γέφυρα μεταξύ του legacy μηχανισμού και του σύγχρονου αντιδραστικού προγραμματισμού.
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 χρησιμοποιήστε queue: .main στο block-based addObserver ή DispatchQueue.main.async εντός του χειριστή. Το Combine publisher με receive(on:) λύνει επίσης το πρόβλημα νήματος.
Selector-based: crash EXC_BAD_ACCESS κατά την αποστολή ειδοποίησης μετά την αποδέσμευση του παρατηρητή. Block-based (iOS 9+): δεν υπάρχει διαρροή χάρη στην αδύναμη αναφορά, αλλά το κέντρο ειδοποιήσεων συνεχίζει να διατηρεί το block στη μνήμη μέχρι ρητό removeObserver. Συνιστάται πάντα η διαγραφή του παρατηρητή στο deinit ή η χρήση του προτύπου Token για αυτόματη διαχείριση.
NotificationCenter — εκπομπή αυθαίρετων συμβάντων μεταξύ μη συνδεδεμένων στοιχείων. KVO — παρακολούθηση αλλαγής συγκεκριμένης ιδιότητας συγκεκριμένου αντικειμένου. Το KVO απαιτεί κληρονομικότητα από NSObject και ειδοποιεί αυτόματα κατά την αλλαγή ιδιότητας μέσω setter. Το NotificationCenter ειδοποιεί μόνο με ρητή κλήση post. Για παρακολούθηση μοντέλου, προτιμάται το KVO ή το Combine.
Ένα default center ανά διεργασία εφαρμογής. Πρόσθετα κέντρα μπορούν να δημιουργηθούν μέσω NotificationCenter(), αλλά στην πράξη χρησιμοποιείται το κοινόχρηστο default. Κάθε κέντρο λειτουργεί ανεξάρτητα — το post στο ένα δεν παραδίδεται σε παρατηρητές του άλλου. Για απομόνωση μονάδων, χρησιμοποιήστε ξεχωριστούς χώρους ονομάτων Name μέσω αντίστροφων-τομέα ονομάτων ειδοποιήσεων.
Μερικώς. Το Combine παρέχει το NotificationCenter.Publisher που τυλίγει το NotificationCenter σε ένα αντιδραστικό ρεύμα. Το Combine λύνει το πρόβλημα συγχρονικότητας (μέσω receive(on:)), προσθέτει τελεστές μετασχηματισμού και αυτόματη διαχείριση εγγραφών (AnyCancellable). Ωστόσο, το NotificationCenter παραμένει για ειδοποιήσεις συστήματος iOS (UIApplication, UIKeyboard) και legacy κώδικα. Το Combine είναι ένα επιπλέον επίπεδο, όχι αντικατάσταση.
Περίληψη
Θα αναπτύξουμε μια εφαρμογή για κινητά έτοιμη για χρήση
Η IT Sectr δημιουργεί εφαρμογές iOS και Android για νεοφυείς επιχειρήσεις και επιχειρήσεις από το 2017. Θα σας συμβουλεύσουμε και θα προτείνουμε την καλύτερη λύση.
Διαβάστε επίσης