NotificationCenter — ماهیت، اصل کار و معماری اعلان‌ها

نویسنده: IT Sectr منتشر شده: 2026-03-18 زمان مطالعه: 10 دقیقه

NotificationCenter — یک مکانیزم سیستمی iOS برای ارسال و دریافت اعلان‌ها بین مؤلفه‌های برنامه بدون ارتباط مستقیم بین فرستنده و گیرنده است. NotificationCenter مبتنی بر الگوی Observer، به اشیا امکان اشتراک در رویدادها و واکنش ناهمگام به آنها را می‌دهد. طبق Apple Documentation (2025)، NSNotificationCenter از ارسال همگام اعلان‌ها از طریق post(name:object:) و همچنین ارسال تأخیری از طریق NotificationQueue پشتیبانی می‌کند. مرکز اعلان در چارچوب یک فرآیند کار می‌کند و از مرزهای برنامه عبور نمی‌کند.

نکات اصلی

  • NotificationCenter — پیاده‌سازی الگوی Observer برای تبادل رویدادها بین مؤلفه‌های iOS است.
  • addObserver یک شی را برای اعلان‌های با نام مشخص و شی فرستنده اشتراک می‌کند.
  • post(name:object:) اعلان را به همه ناظران مشترک به صورت همگام ارسال می‌کند.
  • removeObserver برای فراخوانی در deinit الزامی است، در غیر این صورت هنگام ارسال اعلان crash رخ می‌دهد.
  • NotificationQueue امکان تأخیر اعلان‌ها را برای تحویل ناهمگام فراهم می‌کند.

NotificationCenter چیست؟

NotificationCenter (NSNotificationCenter) — یک مکانیزم داخلی iOS برای پیاده‌سازی ارتباط ضعیف بین اشیا است. الگوی Observer به یک شی (فرستنده) اجازه می‌دهد بدون مرجع مستقیم، بسیاری از اشیای دیگر (ناظران) را از وقوع یک رویداد مطلع کند. NotificationCenter با سه موجودیت کار می‌کند: Notification.Name (شناسه اعلان)، Notification (ظرف حاوی داده) و NotificationCenter (توزیع‌کننده). هر برنامه یک default center مشترک دارد.

NSNotification و Notification.Name

Notification.Name — ساختاری است که نوع اعلان را شناسایی می‌کند. از طریق extension Name: Notification.Name("MyNotification") ایجاد می‌شود. Notification — شیء حاوی name، object (فرستنده) و userInfo (فرهنگ لغت با داده) است. اعلان‌های سیستمی به عنوان ثابت اعلام شده‌اند: UIApplication.didBecomeActiveNotification، UIResponder.keyboardWillShowNotification. اعلان‌های سفارشی باید برای جلوگیری از تداخل نام‌ها از طریق extension گروه‌بندی شوند. نام‌ها باید معکوس-دامنه باشند.

swift
// تعریف اعلان‌های سفارشی
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)

ناظر از طریق متد addObserver(_:selector:name:object:) در اعلان مشترک می‌شود. Selector — متدی است که هنگام دریافت اعلان فراخوانی می‌شود. پارامتر object امکان فیلتر کردن اعلان‌های یک فرستنده مشخص را فراهم می‌کند. اگر object برابر nil باشد، ناظر همه اعلان‌های با نام مشخص را از هر فرستنده‌ای دریافت می‌کند. از iOS 9 به بعد، addObserver برای block-based API نیاز به حذف دستی ندارد، اما selector-based همچنان به removeObserver نیاز دارد.

swift
// اشتراک در اعلان (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 چگونه کار می‌کند؟

NotificationCenter یک جدول نگاشت (name ← مجموعه ناظران) نگهداری می‌کند. وقتی فرستنده post(name:object:) را فراخوانی می‌کند، مرکز اعلان به صورت همگام همه ناظران مشترک در این نام را پیمایش کرده و selector یا بلوک‌های آنها را فراخوانی می‌کند. ویژگی کلیدی: post نخ فعلی را تا پایان همه handlerها مسدود می‌کند. اگر handlerها عملیات سنگین انجام دهند، این کار فرستنده را به تأخیر می‌اندازد. NotificationQueue این مشکل را با تأخیر در تحویل اعلان‌ها حل می‌کند.

ارسال همگام (post)

متد post(name:object:userInfo:) اعلان را بلافاصله به همه ناظران ارسال می‌کند. فراخوانی همگام است — کد بعد از post فقط پس از اتمام همه handlerها اجرا می‌شود. ترتیب فراخوانی ناظران تضمین نشده و ممکن است بین اجراها تغییر کند. برای پردازش ترتیبی از NotificationQueue با coalescing استفاده کنید. post را در داخل handler همان اعلان فراخوانی نکنید — این منجر به بازگشت بی‌نهایت می‌شود.

ارسال تأخیری (NotificationQueue)

NotificationQueue اعلان‌ها را برای تحویل ناهمگام به صف اضافه می‌کند. از coalescing (ادغام اعلان‌های یکسان) و انتخاب صف تحویل (asap، idle، modal) پشتیبانی می‌کند. Coalescing برای رویدادهای مکرر (وضعیت بارگذاری) مفید است، زمانی که فقط باید با آخرین مقدار اعلان داد. NotificationQueue از run loop برای فعال شدن استفاده می‌کند، بنابراین فقط در نخ‌های با run loop فعال کار می‌کند.

swift
// ارسال تأخیری از طریق 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)
}

Notification در مقابل Delegate در مقابل KVO

iOS سه مکانیزم اصلی برای ارتباط بین اشیا فراهم می‌کند: NotificationCenter، Delegate و KVO (Key-Value Observing). هر کدام مشکل اعلان را حل می‌کنند، اما با مصالحه‌های متفاوتی از نظر میزان وابستگی، عملکرد و امنیت نوع. انتخاب مکانیزم به رابطه «یک-به-یک» یا «یک-به-چند» و نیاز به انتقال داده بستگی دارد.

ویژگیNotificationCenterDelegateKVO
وابستگیضعیف (نام اعلان)قوی (پروتکل)متوسط (کلید)
رابطهیک-به-چندیک-به-یکیک-به-چند
امنیت نوعپایین (userInfo به عنوان Dictionary)بالا (متدهای پروتکل)متوسط (Any?)
عملکردمتوسط (پیمایش جدول)بالا (فراخوانی مستقیم)پایین (NSObject)
ناهمگامیهمگام (post مسدود می‌کند)همگام در نخ فرستندههمگام هنگام تغییر

چه زمانی NotificationCenter را انتخاب کنیم

NotificationCenter برای رویدادهایی ایده‌آل است که چندین مؤلفه مستقل باید به آنها واکنش نشان دهند. مثال‌ها: تغییر تنظیمات برنامه، خروج کاربر از حساب، دریافت اعلان push در پس‌زمینه. NotificationCenter همچنین برای ماژول‌های ضعیف مناسب است (ویژگی A نباید از ویژگی B مطلع باشد). نقطه ضعف — عدم امنیت نوع: کلیدهای userInfo رشته‌ای هستند، نه enum.

چه زمانی Delegate یا KVO را انتخاب کنیم

Delegate را برای رابطه یک-به-یک با قرارداد واضح انتخاب کنید (tableView.delegate). Delegate سریع‌تر و از نظر نوع امن‌تر است. KVO را برای مشاهده تغییر یک ویژگی خاص مدل انتخاب کنید (isLoading، progress). KVO نیاز به ارث‌بری از NSObject دارد و ممکن است در اشکال‌زدایی مشکل ایجاد کند (رشته‌های جادویی کلید). در Swift مدرن، Combine و async sequences هر سه رویکرد را جایگزین می‌کنند.

AddObserver: اعلان‌های همگام و ناهمگام

متد addObserver از دو نوع اشتراک پشتیبانی می‌کند: selector-based (سنتی) و block-based (با closure). Selector-based نیاز به سازگاری @objc و حذف دستی ناظر دارد. Block-based (iOS 9+) امکان استفاده از capture list را می‌دهد و هنگام استفاده از بلوک‌های بدون ارجاع قوی، به طور خودکار توسط OS مدیریت می‌شود. Block-based همچنین از queue پشتیبانی می‌کند — ناظر اعلان را در صف مشخص شده دریافت می‌کند.

Selector-based addObserver

روش سنتی اشتراک از طریق selector. متد handler باید با @objc مشخص شده و یک Notification اختیاری دریافت کند. مزیت: قابلیت استفاده توسط هر کلاسی، از جمله legacy Objective-C. معایب: عدم امنیت نوع selector، خطر اشتباه تایپی در نام selector، removeObserver اجباری در deinit. اگر ناظر زودتر از شی حذف شود، handler فراخوانی نخواهد شد.

Block-based addObserver

Block-based API یک closure می‌پذیرد که هنگام دریافت اعلان اجرا می‌شود. پارامتر queue مشخص می‌کند که بلوک در کدام صف اجرا شود — main queue برای به‌روزرسانی UI یا background queue برای پردازش داده. مقدار بازگشتی NSObjectProtocol برای حذف ناظر استفاده می‌شود: NotificationCenter.default.removeObserver(observer). Block-based در Swift مدرن ترجیح داده می‌شود.

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. اگر ناظر قبل از deallocation حذف نشود، هنگام ارسال اعلان مرکز سعی می‌کند متد شیء آزاد شده را فراخوانی کند که منجر به EXC_BAD_ACCESS می‌شود. از iOS 9 به بعد، block-based addObserver از ارجاعات ضعیف استفاده می‌کند، اما selector-based همچنان به removeObserver دستی نیاز دارد. Best practice: حذف ناظر در deinit.

چه زمانی removeObserver را فراخوانی کنیم

Selector-based: حتماً NotificationCenter.default.removeObserver(self) را در deinit فراخوانی کنید. اگر ناظر در چند اعلان مشترک است، می‌توان همه را یکجا (بدون پارامتر) یا یک مورد خاص را با نام حذف کرد. Block-based: از طریق removeObserver با token دریافت شده از addObserver حذف کنید. برای block-based در iOS 9+ نشت رخ نمی‌دهد، اما حذف همچنان برای عملکرد توصیه می‌شود: ناظران آزاد شده در هنگام post پیمایش نخواهند شد.

swift
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 مدیریت ناظران را خودکار می‌کند. هنگام اشتراک، یک شی token (NSObjectProtocol) بازگردانده می‌شود که در زمان deallocation به طور خودکار ناظر را حذف می‌کند. NotificationTokenWrapper یک ارجاع ضعیف به NotificationCenter و token ناظر نگه می‌دارد و removeObserver را در deinit فراخوانی می‌کند. این کار NotificationCenter را به رویکرد Combine نزدیک می‌کند، جایی که AnyCancellable چرخه عمر اشتراک را مدیریت می‌کند.

NotificationCenter در محیط چندنخی

Thread safety NotificationCenter تضمین می‌کند که post می‌تواند از هر نخی فراخوانی شود و همه ناظران اعلان را در همان نخی که post فراخوانی شده دریافت می‌کنند. این برای برنامه‌های چندنخی حیاتی است: اگر اعلان از نخ پس‌زمینه ارسال شود، handlerها نیز در نخ پس‌زمینه اجرا می‌شوند. برای به‌روزرسانی UI باید پردازش را از طریق DispatchQueue.main.async به main queue ارسال کرد.

ایمنی نخ post و addObserver

NotificationCenter برای فراخوانی‌های post و addObserver از نخ‌های مختلف ایمن است. همگام‌سازی داخلی از قفل استفاده می‌کند، بنابراین postهای مکرر از چند نخ می‌توانند contention ایجاد کنند. برای سناریوهای با بار بالا (وضعیت بارگذاری ۱۰۰۰ فایل) از صف اعلان جداگانه یا Combine publisher استفاده کنید. NotificationQueue با postingStyle .now معادل post مستقیم است.

تحویل ناهمگام از طریق Combine

NotificationCenter از Combine publisher از طریق NotificationCenter.default.publisher(for:object:) پشتیبانی می‌کند. Publisher هر اعلان را به یک رویداد Combine تبدیل می‌کند که می‌توان از طریق map، filter، debounce و throttle تبدیل کرد. این مشکل تحویل همگام را حل می‌کند: Combine اعلان‌ها را به صورت ناهمگام در Scheduler مشخص شده پردازش می‌کند. NotificationCenter.publisher — پلی بین مکانیزم legacy و برنامه‌نویسی واکنشی مدرن است.

swift
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 ایمنی نخ دارد؟

بله، NotificationCenter برای فراخوانی‌های post و addObserver از هر نخی ایمن است. با این حال handlerها در همان نخی که post فراخوانی شده اجرا می‌شوند. برای به‌روزرسانی UI از queue: .main در block-based addObserver یا DispatchQueue.main.async در داخل handler استفاده کنید. Combine publisher با receive(on:) نیز مشکل نخ را حل می‌کند.

اگر ناظر حذف نشود چه اتفاقی می‌افتد؟

Selector-based: crash EXC_BAD_ACCESS هنگام ارسال اعلان پس از deallocation ناظر. Block-based (iOS 9+): به دلیل ارجاع ضعیف نشتی وجود ندارد، اما مرکز اعلان همچنان بلوک را در حافظه تا removeObserver صریح نگه می‌دارد. توصیه می‌شود همیشه ناظر را در deinit حذف کنید یا از الگوی Token برای مدیریت خودکار استفاده کنید.

تفاوت بین NotificationCenter و KVO چیست؟

NotificationCenter — پخش رویدادهای دلخواه بین مؤلفه‌های غیرمرتبط. KVO — مشاهده تغییر یک ویژگی خاص یک شی خاص. KVO نیاز به ارث‌بری NSObject دارد و هنگام تغییر ویژگی از طریق setter به طور خودکار اعلان می‌دهد. NotificationCenter فقط با فراخوانی صریح post اعلان می‌دهد. برای مشاهده مدل، KVO یا Combine ترجیح داده می‌شود.

چند NotificationCenter در یک برنامه وجود دارد؟

یک default center برای هر فرآیند برنامه. مراکز اضافی را می‌توان از طریق NotificationCenter() ایجاد کرد، اما در عمل از default مشترک استفاده می‌شود. هر مرکز مستقل کار می‌کند — post در یکی به ناظران دیگری تحویل داده نمی‌شود. برای ایزوله‌سازی ماژول‌ها از فضاهای نام جداگانه Name از طریق نام‌های معکوس-دامنه اعلان‌ها استفاده کنید.

آیا Combine جایگزین NotificationCenter می‌شود؟

تا حدی. Combine ارائه‌دهنده NotificationCenter.Publisher است که NotificationCenter را در یک جریان واکنشی می‌پیچد. Combine مشکل همگامی (از طریق receive(on:)) را حل می‌کند، عملگرهای تبدیل و مدیریت خودکار اشتراک (AnyCancellable) را اضافه می‌کند. با این حال NotificationCenter برای اعلان‌های سیستمی iOS (UIApplication، UIKeyboard) و کد legacy باقی می‌ماند. Combine یک لایه اضافی است، نه جایگزین.

خلاصه

  • NotificationCenter — پیاده‌سازی الگوی Observer برای ارتباط ضعیف «یک-به-چند» در iOS.
  • post اعلان را به صورت همگام به همه ناظران در نخ فعلی ارسال می‌کند و فرستنده را مسدود می‌کند.
  • addObserver از اشتراک selector-based (با @objc) و block-based (با capture list و queue) پشتیبانی می‌کند.
  • removeObserver برای اشتراک‌های selector-based در deinit اجباری است، در غیر این صورت crash رخ می‌دهد.
  • NotificationQueue تحویل تأخیری با coalescing برای رویدادهای مکرر فراهم می‌کند.
  • Thread safety کار از هر نخی را تضمین می‌کند، اما handlerها در نخ فرستنده اجرا می‌شوند.
  • برای مدیریت ایمن و مدرن اشتراک‌ها از الگوی Token یا Combine publisher استفاده کنید.

ما یک اپلیکیشن موبایل به صورت کلید در دست توسعه خواهیم داد

IT Sectr از سال 2017 برنامه‌های iOS و Android را برای استارتاپ‌ها و کسب‌وکارها ایجاد می‌کند. ما به شما مشاوره می‌دهیم و بهترین راه‌حل را پیشنهاد خواهیم کرد.

بحث درباره پروژه

همچنین بخوانید