(本人总结,如有不对,请多多指正)
自从cocos2dx 3.x后,NotificationCenter就标记为废弃了,而是用EventListenerCustom代替
NotificationCenter 实现很简单,里面有着数组NotificationObserver
当你注册了一个消息的同时,也注册了消息所响应的函数,这些包含在NotificationObserver类中,当有消息发出时,NotificationCenter就循环NotificationObserver
,如果名称对应,结点对应,就直接调用函数,代码如下
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);
}
}
}
而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<TransitionScene*>(_runningScene) != nullptr;
bool newIsTransition = dynamic_cast<TransitionScene*>(_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只有在场景跑起来后才可以响应