UI和代码结合的弊端
1.大量通过界面引用UI节点
如下图所示,通过脚本组件大量直接引用UI节点,会在相应的描述文件中记录相应的key和value,在一定程度上会增加相关描述文件的大小,从而影响这个描述文件加载所花费的时间。进一步影响资源加载总时间。
{
"__type__": "85d24iUBchG45eRtzPrngjE",
"_name": "",
"_objFlags": 0,
"node": {
"__id__": 1
},
"_enabled": true,
"label": {
"__id__": 7
},
"node1": {
"__id__": 9
},
"node2": {
"__id__": 12
},
"_id": ""}
通过脚本组件大量直接引用UI节点这种方式还有一个弊端,当节点上的脚本组件因某种原因丢失或者说被重置了,那么还需要被重新挂载一次,如果时间长了,可能都忘记了谁对应的谁。
2.代码中大量查找节点
第一种方式,代码中通过 getChildByName, getChildByUuid 函数查到相应的节点, 在此以 getChildByName 函数来举例。通过下面代码可以看出,正序遍历查找相应节点的名字,返回第一个查到的节点。
/**
* !#en Returns a child from the container given its name.
* !#zh 通过名称获取节点的子节点。
* @method getChildByName
* @param {String} name - A name to find the child node.
* @return {Node} a CCNode object whose name equals to the input parameter
* @example
* var child = node.getChildByName("Test Node");
*/
getChildByName (name) {
if (!name) {
cc.log("Invalid name");
return null;
}
var locChildren = this._children;
for (var i = 0, len = locChildren.length; i < len; i++) {
if (locChildren[i]._name === name)
return locChildren[i];
}
return null;
}
举个例子:如下所示,一个根节点下依次有节点A,节点B,节点C,节点D 四个子节点。现在通过 getChildByName 来获取 节点D。
--根节点
----子节点A
----子节点B
----子节点C
----子节点D
如果现在需要查找子节点A呢:
第一次遍历: 查找子节点A,满足条件,则返回子节点A
也就是说 getChildByName 查找子节点A的时间复杂度为1。
如果现在需要查找子节点D呢:
第一次遍历: 查找子节点A,不满足条件,则继续遍历查找
第二次遍历: 查找子节点B,不满足条件,则继续遍历查找
第三次遍历: 查找子节点C,不满足条件,则继续遍历查找
第四次遍历: 查找子节点D,不满足条件,则返回子节点D
也就是说 getChildByName 查找子节点A的时间复杂度为4。
综上所述: getChildByName 查找相应的节点的时间复杂度为 N。意味得查找的节点广度大,所需要的时间越长。代码中大量通过 getChildByName , getChildByUuid 函数查到相应的节点是一个影响性能的因子。
第二种方式,代码中通过 cc.find 函数查到相应的节点,从下面代码可以看出,两层正序遍历查找相应的节点。第一层正序遍历的因子是传入的路径分割。第二层正序遍历的因子是节点的children。
/**
* Finds a node by hierarchy path, the path is case-sensitive.
* It will traverse the hierarchy by splitting the path using '/' character.
* This function will still returns the node even if it is inactive.
* It is recommended to not use this function every frame instead cache the result at startup.
*
* @method find
* @static
* @param {String} path
* @param {Node} [referenceNode]
* @return {Node|null} the node or null if not found
*/
cc.find = module.exports = function (path, referenceNode) {
if (path == null) {
cc.errorID(5600);
return null;
}
if (!referenceNode) {
var scene = cc.director.getScene();
if (!scene) {
if (CC_DEV) {
cc.warnID(5601);
}
return null;
}
else if (CC_DEV && !scene.isValid) {
cc.warnID(5602);
return null;
}
referenceNode = scene;
}
else if (CC_DEV && !referenceNode.isValid) {
cc.warnID(5603);
return null;
}
var match = referenceNode;
var startIndex = (path[0] !== '/') ? 0 : 1; // skip first '/'
var nameList = path.split('/');
// parse path
for (var n = startIndex; n < nameList.length; n++) {
var name = nameList[n];
var children = match._children;
match = null;
for (var t = 0, len = children.length; t < len; ++t) {
var subChild = children[t];
if (subChild.name === name) {
match = subChild;
break;
}
}
if (!match) {
return null;
}
}
return match;
};
举个例子:如下所示,现在一个场景上的节点关系如下。
-- 场景根节点
---- 子节点A
----- 子节点A1
--- 子节点B
--- 子节点C
----- 子节点C1
--- 子节点D
----- 子节点D1
----- 子节点D2
如果现在需要查找子节点A1呢(暂不考虑第二个参数的情况下):
cc.find('子节点A/子节点A1') 开始遍历是 names = [子节点A,子节点A1]
第一层第一次遍历: 找到子节点A,满足条件,则进行第二层遍历。
第二层第一次遍历: 找到子节点A1,满足条件,则返回。
也就是说 cc.find 查找子节点A1 的时间复杂度为2。
如果现在需要查找子节点D2呢(暂不考虑第二个参数的情况下):
cc.find(‘子节点D/子节点D2’) 开始遍历是 names = [子节点D,子节点D2]
第一层第一次遍历: 找到子节点A,不满足条件,则继续遍历。
第一层第一次遍历: 找到子节点B,不满足条件,则继续遍历。
第一层第一次遍历: 找到子节点C,不满足条件,则继续遍历。
第一层第一次遍历: 找到子节点D,满足条件,则进行第二层遍历。
第二层第一次遍历: 找到子节点D1,不满足条件,则继续。
第二层第一次遍历: 找到子节点D2,满足条件,则返回。
也就是说 cc.find 查找子节点D2的时间复杂度为6
综上所述: cc.find 查找一次节点的时间复杂度为 C(2 n)=n*(n-1)/(2*1)。意味着查找的节点广度越大和深度越深所需要的时间越长。代码中大量通过 cc.find 函数查到相应的节点是一个影响性能的因子。
无论是第一种方式还是第二种方式在代码中引用相关的节点,如果由于某种原因,修改了节点之间的关系,进而查不到相应的节点引发的错误。还需要重新修改查找相应节点的路径。
3. 大量通过界面引用UI节点 和 代码中大量查找节点
通过这种方式来查找节点,同时具有上面两种方法的弊端。更重要的是同时通过界面引用UI节点和代码中查找UI节点,是不是感觉有点乱。