NotificationCenter — суштина, принцип рада и архитектура обавештења

Аутор: IT Sectr Објављено: 2026-03-18 Време читања: 10 мин

NotificationCenter — је системски механизам iOS-а за слање и примање обавештења између компоненти апликације без директне везе између пошиљаоца и примаоца. Заснован на шаблону Observer, NotificationCenter омогућава објектима да се претплате на догађаје и реагују на њих асинхроно. Према 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 (диспечер). Свака апликација има shared 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:). Селектор — метода која ће бити позвана при пријему обавештења. Параметар 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:), центар за обавештења синхроно обилази све посматраче претплаћене на ово име и позива њихове селекторе или блокове. Кључна карактеристика: 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 vs Delegate vs 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 (са затварањем). Selector-based захтева @objc компатибилност и ручно уклањање посматрача. Block-based (iOS 9+) омогућава коришћење capture list-е и аутоматски се управља од стране OS-а при коришћењу блокова без јаких референци. Block-based такође подржава queue — посматрач прима обавештење у наведеном реду.

Selector-based addObserver

Традиционални начин претплате путем селектора. Метода handler мора бити означена са @objc и примати опциони Notification. Предност: могућност коришћења од стране било које класе, укључујући legacy Objective-C. Недостаци: недостатак сигурности типова селектора, ризик од грешака у куцању имена селектора, обавезни removeObserver у deinit. Ако се посматрач уклони пре објекта, handler неће бити позван.

Block-based addObserver

Block-based API прима затварање које се извршава при пријему обавештења. Параметар 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-ом. Ако посматрач није уклоњен пре деалокације, при слању обавештења центар ће покушати да позове метод већ ослобођеног објекта, што доводи до 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 са токеном добијеним од 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 шаблон аутоматизује управљање посматрачима. При претплати враћа се објекат-токен (NSObjectProtocol), који при деалокацији аутоматски уклања посматрача. NotificationTokenWrapper чува слабу референцу на NotificationCenter и токен посматрача, позивајући removeObserver у deinit. Ово приближава NotificationCenter Combine приступу, где AnyCancellable управља животним циклусом претплате.

NotificationCenter у вишенитном окружењу

Thread safety NotificationCenter гарантује да post може бити позван из било које нити и да ће сви посматрачи примити обавештење у истој нити у којој је post позван. Ово је критично за вишенитне апликације: ако је обавештење послато из позадинске нити, handler-и ће се такође извршити у позадинској нити. За ажурирање UI-ја потребно је диспечирати обраду на main queue путем DispatchQueue.main.async.

Сигурност нити за post и addObserver

NotificationCenter је сигуран за нити за позиве post и addObserver из различитих нити. Унутрашња синхронизација користи закључавање, па чести post-ови из више нити могу створити contention. За сценарије са високим оптерећењем (напредак учитавања 1000 датотека) користите посебан ред обавештења или 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 при слању обавештења након деалокације посматрача. 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 је обавезан у deinit за selector-based претплате, у супротном crash.
  • NotificationQueue обезбеђује одложену доставу са coalescing-ом за честе догађаје.
  • Thread safety гарантује рад из било које нити, али handler-и се извршавају у нити пошиљаоца.
  • Користите Token шаблон или Combine publisher за безбедно и модерно управљање претплатама.

Развићемо мобилну апликацију под кључ

IT Sectr креира iOS и Android апликације за стартапе и предузећа од 2017. године. Саветоваћемо вас и предложити најбоље решење.

Разговарајте о пројекту

Прочитајте такође