cocos2dx EventListenerCustom 和NotificationCenter的优缺点

(本人总结,如有不对,请多多指正)

自从cocos2dx 3.x后,NotificationCenter就标记为废弃了,而是用EventListenerCustom代替

NotificationCenter 实现很简单,里面有着数组NotificationObserver

当你注册了一个消息的同时,也注册了消息所响应的函数,这些包含在NotificationObserver类中,当有消息发出时,NotificationCenter就循环NotificationObserver

,如果名称对应,结点对应,就直接调用函数,代码如下

CCARRAY_FOREACH(ObserversCopy, obj)
    {
        NotificationObserver* observer = static_cast(obj);
        if (!observer)
            continue;
        
        if (observer->getName() == name && (observer->getSender() == sender || observer->getSender() == nullptr || sender == nullptr))
        {
            if (0 == observer->getHandler())
            {
                observer->performSelector(sender);
            }
        }
    }

而EventListenerCustom就不同,和EventListenerTouchOneByOne等相同,它们内部是通过EventDispatcher发送和响应消息的。EventDispatcher是单例类,保存在Director中,其他Node保存的只是它的引用。通过EventListenerCuston发送消息有一个弊端,就是消息有可能不能被接收,即当你发送消息而对应的函数可能没有被调用。

看Node中的onEnter代码

void Node::onEnter()
{
#if CC_ENABLE_SCRIPT_BINDING
    if (_scriptType == kScriptTypeJavascript)
    {
        if (ScriptEngineManager::sendNodeEventToJS(this, kNodeOnEnter))
            return;
    }
#endif
    
    if (_onEnterCallback)
        _onEnterCallback();

    if (_componentContainer && !_componentContainer->isEmpty())
    {
        _componentContainer->onEnter();
    }
    
    _isTransitionFinished = false;
    
    for( const auto &child: _children)
        child->onEnter();
    
    this->resume();//重点是这句
    
    _running = true;
    
#if CC_ENABLE_SCRIPT_BINDING
    if (_scriptType == kScriptTypeLua)
    {
        ScriptEngineManager::sendNodeEventToLua(this, kNodeOnEnter);
    }
#endif
}

接下来看看this->resume()做了什么

void Node::resume()
{
    _scheduler->resumeTarget(this);
    _actionManager->resumeTarget(this);
    _eventDispatcher->resumeEventListenersForTarget(this);//重点
}
一般情况下 Node::onEnter()是在addChild中是被调用的,而场景类的onEnter()是在切换场景是被调用的,如下

void Director::setNextScene()
{
    bool runningIsTransition = dynamic_cast(_runningScene) != nullptr;
    bool newIsTransition = dynamic_cast(_nextScene) != nullptr;

    // If it is not a transition, call onExit/cleanup
     if (! newIsTransition)
     {
         if (_runningScene)
         {
             _runningScene->onExitTransitionDidStart();
             _runningScene->onExit();
         }
 
         // issue #709. the root node (scene) should receive the cleanup message too
         // otherwise it might be leaked.
         if (_sendCleanupToScene && _runningScene)
         {
             _runningScene->cleanup();
         }
     }

    if (_runningScene)
    {
        _runningScene->release();
    }
    _runningScene = _nextScene;
    _nextScene->retain();
    _nextScene = nullptr;

    if ((! runningIsTransition) && _runningScene)
    {
        _runningScene->onEnter();//重点
        _runningScene->onEnterTransitionDidFinish();
    }
}

也就是说,你在层或者场景的init里是无法通过EventListenerCustom发送事件的。因为当时的事件是暂停的

if (sceneGraphPriorityListeners)
    {
        if (!shouldStopPropagation)
        {
            // priority == 0, scene graph priority
            for (auto& l : *sceneGraphPriorityListeners)
            {
                if (l->isEnabled() && !l->isPaused() && l->isRegistered() && onEvent(l))//重点
                {
                    shouldStopPropagation = true;
                    break;
                }
            }
        }
    }

所以个人认为,NotificationCenter和EventListenerCustom并没有孰优孰劣,看个人需求
下面总结相同点和不同点

相同点:都能发送消息从而使对应的函数被调用

不同点 NotificaCenter需要显式地移除,EventListenerCustom只有在场景跑起来后才可以响应


你可能感兴趣的:(cocos2dx)