cocos2d observer pattern : notification center

2023. 3. 14. 19:28cocos2dx

cocos2d Notification은 전체적으로 NotificationCenter singleton 에서 총괄함.

cocos2d::NotificationCenter::getInstance() 을 먼저 받아온 다음에...

여기다가 addObserver를 붙여주고, postNotification으로 해당 observer를 호출.

붙였던 observer는 Ref 객체가 사라지는 타이밍에 removeObserver로 꼭 없애 줘야 함.


----------------------------------------------------------------------------------

#1. Notiinfo.h

#ifndef __NOTIMESSAGE_H__
#define __NOTIMESSAGE_H__

#ifndef SNDNOTI(str, ...)
#define SNDNOTI(str, ...) cocos2d::NotificationCenter::getInstance()->postNotification(str, ##__VA_ARGS__)
#endif

#define MSG_BACKSTAGE2_00 "Message_Back2_phase00"

#endif



#2. LayerBackStage2.cpp

LayerBackStage2::~LayerBackStage2()
{
auto NCtr = NotificationCenter::getInstance();
NCtr->removeObserver(this, MSG_BACKSTAGE2_00);
}

bool LayerBackStage2::init()
{
if (!Layer::init()) return false;
auto NCtr = NotificationCenter::getInstance();
NCtr->addObserver(this, callfuncO_selector(LayerBackStage2::onPhase0), MSG_BACKSTAGE2_00, NULL);

scheduleUpdate();
return true;
}


#3. 아무데서나

SNDNOTI(MSG_BACKSTAGE2_00, cocos2d::Integer::create(call_func_value));

SNDNOTI(MSG_BACKSTAGE2_00);

으로 call 한다.

------------------------------------------------------------------------------


#1. postNotification 형식은 

postNotification(const std::string&, Ref*), 
postNotification(const std::string&) 이다.

notification을 보낼 때, 호출 메시지만 보내느냐 아니면 Ref* sender를 함께 보내느냐를 선택할 수 있다.

CCRef.h에서는 함수 등록 매크로 callfuncO_selector를 

typedef void (Ref::*SEL_CallFuncO)(Ref*);
#define CC_CALLFUNCO_SELECTOR(_SELECTOR) static_cast<cocos2d::SEL_CallFuncO>(&_SELECTOR)
#define callfuncO_selector(_SELECTOR) CC_CALLFUNCO_SELECTOR(_SELECTOR)

으로 정의한다.

그렇기 때문에 옵저버로 등록 가능한 함수는 void function(cocos2d::Ref* pRef) 형식이어야만 한다.



#2. addObserver를 통해 옵저버를 등록할때는 

void __NotificationCenter::addObserver(Ref *target, 
                                       SEL_CallFuncO selector,
                                       const std::string& name,
                                       Ref *sender)
{
    if (this->observerExisted(target, name, sender))
        return;
    
    NotificationObserver *observer = new (std::nothrow) NotificationObserver(target, selector, name, sender);
    if (!observer)
        return;
    
    observer->autorelease();
    _observers->addObject(observer);
}

와 같은 일련의 과정을 거치는데, 일단 난 sender를 null으로 하고, 거기에 value를 넣어서 호출하는 쪽을 선호함.

등록시에 sender가 null이 아니라면, 등록한 특정 sender에서만 해당 notification을 콜 할 수 있다.



#3. addObserver를 통해 NotificationCenter에 등록된 객체와 함수는 postNotification을 통해서 호출되는데,

void __NotificationCenter::postNotification(const std::string& name, Ref *sender)
{
    __Array* ObserversCopy = __Array::createWithCapacity(_observers->count());
    ObserversCopy->addObjectsFromArray(_observers);
    Ref* obj = nullptr;
    CCARRAY_FOREACH(ObserversCopy, obj)
    {
        NotificationObserver* observer = static_cast<NotificationObserver*>(obj);
        if (!observer)
            continue;
        
        if (observer->getName() == name && (observer->getSender() == sender || observer->getSender() == nullptr || sender == nullptr))
        {
            if (0 == observer->getHandler())
            {
                observer->performSelector(sender);
            }
        }
    }
}

의 과정을 통해 등록된 전체 옵저버를 흩어보고, name(객체와 함께 등록한 호출 str 메시지)를 통해서 호출할 놈을 찾는다.

다음에는 sender가 등록되어 있다면 일치하는가? sender가 등록되지 않았는가? sender가 없는가? 를 확인해서

notification을 보낸다.