【已回复】吐槽

不得不说 cocostudio 的
UIHelper CCSReader UIActionManager 这部分代码 结构设计得之差 .
滥用单例模式.
给用户的拓展性全被毁掉了…

楼主辛苦了 感谢精彩吐槽 gui框架也在做设计和结构上的优化,其中也包含楼主提到的这3点,关于ui的action也在优化中 楼主可以加我qq 方便以后交流 271788548:)

  • 本帖最后由 Basic_Auth 于 2013-8-21 00:09 编辑 *
  1. UIActionManager .
    UIActionManager 收集了UI上的动画, 通过 UIActionManager 可以获取和播放动画.
    UIActionManager 的存在真的有必要么 ?

按照解析库的思想 UIWiget 既然保存了当前UI的所有组建 为啥单独动画要交给 UIActionManager 去处理?

你的钱包给我来掌管?

  1. 某个 UIWiget 上面有动画.交给 UIActionManager 后, 销毁掉 UIWiget 对象,那么UIActionManager 保存的将是无效的数据.
  2. UIActionManager::initWithDictionary 也没用做过重复验证. 如同时生成2个一样的UIWiget 不销毁,前一个的UIWiget的动画将被强制销毁,
    也就是说无法控制前一个uiWiget的动画了.这是很严重的问题.
  3. 我有2个不同的 UI 都有动画,交给 UIActionManager 管理,其中一个确认不再需要.需要销毁,就意味着不能单独释放uiAction对象.
    必须等待 2个UI全部销毁,才能安全的清除UIActionManager的数据. UIActionManager没有提供单独的清理方案.如果是 5个UI 10个UI呐 ?

ps: .UIActionManager 抢走了 uiWiget 的饭碗. 如果一定要有 UIActionManager 来统一管理, 那么建议把 UIActionManager 内部封装化,
UIWiget 与 UIActionManager 打交道,销毁 UIWiget 的时候 UIWiget 去找UIActionManager 销毁自己的数据. 不让用户直接接触 UIActionManager.

  • 本帖最后由 Basic_Auth 于 2013-8-21 00:23 编辑 *

暂时就这些. 提供给用户使用的接口应该只需要用户去注意销毁生成的uiWiget对象.而不需要用户去考虑释放
其它地方暂存的资源. 不然以后解析库改变,原先的释放方法不再需要了,那么用户还得再去维护现有代码.

某些地方可能我没有描述清楚,可以私谈.

  • 本帖最后由 Basic_Auth 于 2013-8-20 23:30 编辑 *
  1. CCSReader

CCSReader 的作用是解析json组合成用户需要的UIWiget对象.

class CCSReader : CCObject 

此类继承 CCObject 又是个单例. 请问你用到了CCObject的什么属性?继承他的意义何在?
由于是个单利.而且随时保持有且只有一个CCSReader对象存在.和不会用于多态转换.
那么继承CCOBject 似乎完全没有意义.

void setPropsForWidgetFromJsonDictionary(UIWidget*widget,cs::CSJsonDictionary* options);
    void setColorPropsForWidgetFromJsonDictionary(UIWidget*widget,cs::CSJsonDictionary* options);
    void setPropsForButtonFromJsonDictionary(UIWidget*widget,cs::CSJsonDictionary* options);
    void setPropsForCheckBoxFromJsonDictionary(UIWidget*widget,cs::CSJsonDictionary* options);
    void setPropsForImageViewFromJsonDictionary(UIWidget*widget,cs::CSJsonDictionary* options);
    void setPropsForLabelFromJsonDictionary(UIWidget*widget,cs::CSJsonDictionary* options);
    void setPropsForLabelAtlasFromJsonDictionary(UIWidget*widget,cs::CSJsonDictionary* options);
    void setPropsForContainerWidgetFromJsonDictionary(UIWidget*widget,cs::CSJsonDictionary* options);
    void setPropsForPanelFromJsonDictionary(UIWidget*widget,cs::CSJsonDictionary* options);
    void setPropsForScrollViewFromJsonDictionary(UIWidget*widget,cs::CSJsonDictionary* options);
    void setPropsForSliderFromJsonDictionary(UIWidget*widget,cs::CSJsonDictionary* options);
    void setPropsForTextAreaFromJsonDictionary(UIWidget*widget,cs::CSJsonDictionary* options);
    void setPropsForTextButtonFromJsonDictionary(UIWidget*widget,cs::CSJsonDictionary* options);
    void setPropsForTextFieldFromJsonDictionary(UIWidget*widget,cs::CSJsonDictionary* options);
    void setPropsForLoadingBarFromJsonDictionary(UIWidget*widget,cs::CSJsonDictionary* options);
    void setPropsForImageButtonFromJsonDictionary(UIWidget* widget, cs::CSJsonDictionary* options);
    void setPropsForListViewFromJsonDictionary(UIWidget* widget, cs::CSJsonDictionary* options);
    void setPropsForPageViewFromJsonDictionary(UIWidget*widget,cs::CSJsonDictionary* options);
    void setPropsForLabelBMFontFromJsonDictionary(UIWidget*widget,cs::CSJsonDictionary* options);
    void setPropsForDragPanelFromJsonDictionary(UIWidget*widget,cs::CSJsonDictionary* options);
<p>std::string m_strFilePath;
    bool m_bOlderVersion;</p>

可以看出 此类的主要目的是为了组合新生成UIWiget而存在. 其 m_strFilePath m_bOlderVersion 应该是属于当前UIWiget 的属性.
如果一定需要持有UIWiget的部分参数.请考虑 CCSReader 作为实体类,每个json文件对应一份新的CCSReader 对象. 解析完毕就释放掉CCSReader对象.

一个游戏中,不可能随时时刻都生成新的UIWiget , 所以整个游戏内部使用到 CCSReader 的频率是非常低的.
用户也可能会缓存 UIWiget 对象. CCSReader 作为单例.完全没必要.

,不建议cocostudio保留多份解析json的规则. 原因:
1: 用户同一个UI之会持有一份规则.另一份规则是闲置的.只能增加代码体积.
2: 官方解析库与cocostudio版本都是同时发布的.用户下载新版解析库就不会缺少那点重新导出UI json数据的时间.
3: 用户需要的是稳定的.精辟高效率的代码,不想做成万金油的解析库.

整理后的

<p>
UIWidget* CCSReader::widgetFromJsonFile(const char *fileName)
{
    m_bOlderVersion = false;
    unsigned long size = 0;
    char *des = (char*)(CCFileUtils::sharedFileUtils()->getFileData(fileName,"r" , &size));
    if(NULL == des || strcmp(des, "") == 0)
   {
         printf("read json file%s] error!
", fileName);
         return NULL;
   }
    cs::CSJsonDictionary *jsonDict = new cs::CSJsonDictionary();
    jsonDict->initWithDescription(des);
   //check json file version 
    const char* fileVersion = DICTOOL->getStringValue_json(jsonDict, "version");
    if (!fileVersion || getVersionInteger(fileVersion) < 250)
    {
           m_bOlderVersion = true;
    }
    
    int texturesCount = DICTOOL->getArrayCount_json(jsonDict, "textures");
    const char* file(NULL);
    for (int i=0; i<texturesCount; i++)
    {
        file = DICTOOL->getStringValueFromArray_json(jsonDict, "textures", i);
        CCSpriteFrameCache::sharedSpriteFrameCache()->addSpriteFramesWithFile(file);
    }
    float fileDesignWidth = DICTOOL->getFloatValue_json(jsonDict, "designWidth");
    float fileDesignHeight = DICTOOL->getFloatValue_json(jsonDict, "designHeight");
    cs::CSJsonDictionary* widgetTree = DICTOOL->getSubDictionary_json(jsonDict, "widgetTree");
    UIWidget* widget = widgetFromJsonDictionary(widgetTree);
    if (widget->getContentSize().equals(CCSizeZero))
    {
        UIContainerWidget* rootWidget = dynamic_cast<UIContainerWidget*>(widget);
        rootWidget->setSize(CCSizeMake(fileDesignWidth, fileDesignHeight));
    }
    widget->setFileDesignSize(CCSizeMake(fileDesignWidth, fileDesignHeight));
    cs::CSJsonDictionary* actions = DICTOOL->getSubDictionary_json(jsonDict, "animation");
    CCLOG("file name == %s]",fileName);
    UIActionManager::shareManager()->initWithDictionary(fileName,actions,widget);</p><p>    CC_SAFE_DELETE(widgetTree);
    CC_SAFE_DELETE(actions);
    CC_SAFE_DELETE(jsonDict);
    CC_SAFE_DELETE_ARRAY(des);
    return widget;
}</p>

楼下继续.

  • 本帖最后由 Basic_Auth 于 2013-8-21 00:17 编辑 *
  1. UIHelper 与 UIWidget 的关系.

从UIHelper 可以看出 方法 createWidgetFromJsonFile 可以生成一个新的 UIWidget .

里面的:

seekWidgetByTag() , 
seekWidgetByName () , 
seekActionWidgetByActionTag(),
seekWidgetByRelativeName(). 

属于其公共的通用方法. 那么 UIHelper 的责任主要就是 创建新的UIWidget .
创建出的 UIWidget 对象就应该与UIHelper 再无联系了.
UIHelper 的职责就应该只是让用户创建UIWidget 对象,和提供部分 UIWidget 能用的公共方法.
单独销毁 UIWidget 不会产生内存泄漏.
(难道你买部手机.觉得不好要砸坏,还要去给店家打招呼?)
更令我不解的是

void addSpriteFrame(const char* fileName);
void removeSpriteFrame(const char* fileName);
void removeAllSpriteFrame();

的意义是什么?

从代码addSpriteFrame 可以看到这句:
CCSpriteFrameCache::sharedSpriteFrameCache()->addSpriteFramesWithFile(fileName);
上面排除了重复的文件.
也根本没有发现 有其它地方调用 removeSpriteFrame , removeAllSpriteFrame 方法.
只有 addSpriteFrame 方法被使用到了.
难道这3个方法就只是防止重复添加cache ?

如果真是这样? 那就多此一举了, 因为 CCSpriteFrameCache::addSpriteFramesWithFile(const char *pszPlist) 方法本身就有验证是否重复.

<p>   void setFileDesignWidth(float width);
    float getFileDesignWidth();
    void setFileDesignHeight(float height);
    float getFileDesignHeight();</p><p>   //private:</p><p>    float m_fFileDesignWidth;
    float m_fFileDesignHeight;</p>

的意义何在?
这几个方法要追溯到 CCSReader::widgetFromJsonFile 方法.
方面里面我们看到 :

 float fileDesignWidth = DICTOOL->getFloatValue_json(jsonDict, "designWidth");
    float fileDesignHeight = DICTOOL->getFloatValue_json(jsonDict, "designHeight");
    if (fileDesignWidth <= 0 || fileDesignHeight <= 0) {
        printf("Read design size error!
");
        CCSize winSize = CCDirector::sharedDirector()->getWinSize();
        CCUIHELPER->setFileDesignWidth(winSize.width);
        CCUIHELPER->setFileDesignHeight(winSize.height);
    }
    else
    {
        CCUIHELPER->setFileDesignWidth(fileDesignWidth);
        CCUIHELPER->setFileDesignHeight(fileDesignHeight);
    }

让UIHelper保存了一些 "当前新UIWidget对象"的部分参数. 并且
float getFileDesignWidth();
float getFileDesignHeight();
根本没有用到…??? 直接注释掉没有任何影响… 这个保存起来的意义是什么?

另.UIHelper不是用来保存被生成对象的属性的.

如用户会通过createWidgetFromJsonFile 生成多个新的UIWidget . 那么这几个方法的意义又再哪儿?
因为保存的是最后创建的对象的设计大小 ??

: 本人不建议将UIHelper设计成一个单例模式. 在销毁的时候还有手动去释放, 设计成纯静态工具集更符合他的职责.
因游戏UIHelper的实例将一直存在,随时可能会调用,用来生成新的UI. 所以考虑直接封装成静态工具集.

不建议使用 CCUIHELPER / CCUIHELPER 来简化开发. CCUIHELPER 虽然简化了获取Instance的步骤.
但是不直观.不能一下就看出这个类到底是个什么东西.

CCUIHELPER-> createWidgetFromJsonFile(fileName);

UIHelper::createWidgetFromJsonFile(fileName);

我觉得第二种胜出

改版后的UIHelper :

class UIHelper
{
public:
   static UIWidget* createWidgetFromJsonFile(const char* fileName);
   static UIWidget* seekWidgetByTag(UIWidget* root, int tag);
   static UIWidget* seekWidgetByName(UIWidget* root, const char* name);
   static UIWidget* seekActionWidgetByActionTag(UIWidget* root, int tag);
   static UIWidget* seekWidgetByRelativeName(UIWidget* root, const char* name);
};


.喝口水,楼下继续.

  • 本帖最后由 Basic_Auth 于 2013-8-21 00:13 编辑 *

发此贴的目的是想与开发者沟通交流一下,
我作为cocostudio的用户的感受和体验.
一些得罪之处请见谅.
感谢cocostudio团队带来方便的win32下的编辑器.
遗憾xp不能用 - -

LZ说说看你的想法

— Begin quote from ____

%url%jxhgzs 发表于 2013-8-20 17:37
LZ说说看你的想法

— End quote

吐槽点容我回家整理一下再发出来 现在下班了

LZ说说看你的想法

markmarkmarkmark