onDidLoadFromCCB 生命周期问题






执行顺序差异的根本原因
在浏览器端(HTML5):
- 深度优先遍历:当解析 CCB 文件时,遇到嵌套的 CCB
文件引用会立即递归加载- 在 CCNodeLoader.js:745
中,parsePropTypeCCBFile 会立即调用
myCCBReader.readFileWithCleanUp(false) - 这会递归地加载子 CCB 文件
- 子节点的 controller 先创建和初始化
- 在 CCNodeLoader.js:745
- 执行顺序:
- 主 CCB 开始加载
- 遇到 _ccbConner (子 CCB 引用)
- 立即加载子 CCB -> 创建
PrizePoolConnerController -> 调用其
onDidLoadFromCCB - 继续加载主 CCB
- 主 CCB 加载完成 -> 调用
PrizePoolBetController.onDidLoadFromCCB
在 Native 端(Android/iOS):
- 广度优先处理:Native 的 C++
实现可能使用了不同的加载策略- 先完成整个节点树的构建
- 然后在 cc.BuilderReader.load 中统一处理所有
controller
- 执行顺序:
- 主 CCB 加载,构建完整节点树(包括子 CCB 节点)
- 主控制器的 onDidLoadFromCCB 先被调用
- 在 cc.BuilderReader.load
的后处理阶段(第994-1055行) - 遍历所有需要 controller 的节点
- 创建并初始化子 controller -> 调用
PrizePoolConnerController.onDidLoadFromCCB
关键代码差异:
HTML5 版本(CCNodeLoader.js:744-745):
// 立即递归加载子 CCB
var ccbFileNode =
myCCBReader.readFileWithCleanUp(false);
Native 版本:
- C++ 层的实现可能延迟了子 CCB controller 的创建
- 在 js_bindings_ccbreader.cpp
中,readNodeGraphFromFile 的 C++ 实现策略不同
- Native 端收集节点信息
在 /frameworks/cocos2d-x/cocos/editor-support/cocosbuilder/CCBReader.cpp 的第 285-297
行:
for (auto iter = _animationManagers->begin(); iter != _animationManagers->end();
++iter)
{
Node* pNode = iter->first;
CCBAnimationManager* manager = iter->second;
pNode->setUserObject(manager);
if (_jsControlled)
{
_nodesWithAnimationManagers.pushBack(pNode); // 收集所有带动画管理器的节点
_animationManagersForNodes.pushBack(manager); // 收集对应的动画管理器
}
}
这里 C++ 层收集了所有带有动画管理器的节点,然后通过 JSB 返回给 JavaScript 层。
- JavaScript 端批量处理
在 /frameworks/cocos2d-x/cocos/scripting/js-bindings/script/jsb_cocosbuilder.js 的第
105-168 行:
// 遍历所有带动画管理器的节点
for (var i = 0; i < nodesWithAnimationManagers.length; i++)
{
var innerNode = nodesWithAnimationManagers[i];
var animationManager = animationManagersForNodes[i];
// ... 创建控制器 ...
// 第 165-168 行:调用 onDidLoadFromCCB
if (typeof(controller.onDidLoadFromCCB) == "function")
{
controller.onDidLoadFromCCB();
}
}
执行顺序差异的根源:
关键点:C++ 端在 readNodeGraphFromData
方法完成后,一次性返回了所有节点(包括父节点和所有子节点)的列表。然后 JavaScript
端按照这个列表的顺序逐个调用 onDidLoadFromCCB。
这个列表的顺序取决于 C++ 端 _animationManagers map 的遍历顺序,而这个 map
在构建节点树时是按照广度优先的顺序添加的(先添加父节点,后添加子节点)。
所以:
- Native:所有节点收集完成后,JavaScript 端按列表顺序调用,导致父节点的
onDidLoadFromCCB 先于子节点 - HTML5:每个节点加载完成后立即调用其
onDidLoadFromCCB,深度优先遍历导致子节点先于父节点