在线观看不卡亚洲电影_亚洲妓女99综合网_91青青青亚洲娱乐在线观看_日韩无码高清综合久久

鍍金池/ 教程/ C/ 回調函數(shù) 2
cocos2d-x for js 中的繼承
JS 與 C++ 的交互 1——JS 代碼調用 C++ 代碼
迎接腳本時代的到來
解決在 vs 中修改 js 源文件無效
JS 腳本語言的優(yōu)勢與一些問題
注冊函數(shù)
回調函數(shù) 2
cxx-generator JS 綁定工具
使用 cocos2d-console 工具轉換腳本為字節(jié)碼
hybrid 開發(fā)模式
JS 與 C++ 的交互 2——JS 與 C++ 的“函數(shù)重載”問題
回調函數(shù)1——按鍵回調
Google 的繼承寫法解析
John Resiq 的繼承寫法解析
JS 與 C++ 的交互 3——C++ 和 JS 類型轉換
傀儡構造函數(shù)

回調函數(shù) 2

上一篇我們講了按鍵回調,這一次我們來說說各種邏輯上的回調函數(shù)。

Cocos2d-x 里面一共有三大類回調函數(shù),第一是按鍵回調 CCMenu 相關的,第二類是定時器相關的回調 Schedule,第三類是 Action 相關的回調 CallFunc。這些回調從最初的引擎版本中就存在著,一直到現(xiàn)在。

一、綁定代碼

在 JSB 的解決方案中,對于后兩類函數(shù),引擎統(tǒng)一封裝成 JSCallbackWrapper 及其子類。

class JSCallbackWrapper: public cocos2d::Object {
public:
    JSCallbackWrapper();
    virtual ~JSCallbackWrapper();
    void setJSCallbackFunc(jsval obj);
    void setJSCallbackThis(jsval thisObj);
    void setJSExtraData(jsval data);

    const jsval& getJSCallbackFunc() const;
    const jsval& getJSCallbackThis() const;
    const jsval& getJSExtraData() const;
protected:
    jsval _jsCallback;
    jsval _jsThisObj;
    jsval _extraData;
};

JSCallbackWrapper 從名字就可以知道,是 JS 回調函數(shù)的包裝器。三個接口也一目了然,回調函數(shù),this,外部數(shù)據(jù)。

// cc.CallFunc.create( func, this, [data])
// cc.CallFunc.create( func )
static JSBool js_callFunc(JSContext *cx, uint32_t argc, jsval *vp)
{
    if (argc >= 1 && argc <= 3) {
        jsval *argv = JS_ARGV(cx, vp);
        std::shared_ptr<JSCallbackWrapper> tmpCobj(new JSCallbackWrapper());

        tmpCobj->setJSCallbackFunc(argv[0]);
        if(argc >= 2) {
            tmpCobj->setJSCallbackThis(argv[1]);
        } if(argc == 3) {
            tmpCobj->setJSExtraData(argv[2]);
        }

        CallFuncN *ret = CallFuncN::create([=](Node* sender){
            const jsval& jsvalThis = tmpCobj->getJSCallbackThis();
            const jsval& jsvalCallback = tmpCobj->getJSCallbackFunc();
            const jsval& jsvalExtraData = tmpCobj->getJSExtraData();

            bool hasExtraData = !JSVAL_IS_VOID(jsvalExtraData);
            JSObject* thisObj = JSVAL_IS_VOID(jsvalThis) ? nullptr : JSVAL_TO_OBJECT(jsvalThis);

            JSB_AUTOCOMPARTMENT_WITH_GLOBAL_OBJCET

            js_proxy_t *proxy = js_get_or_create_proxy<cocos2d::Node>(cx, sender);

            jsval retval;
            if(jsvalCallback != JSVAL_VOID)
            {
                if (hasExtraData)
                {
                    jsval valArr[2];
                    valArr[0] = OBJECT_TO_JSVAL(proxy->obj);
                    valArr[1] = jsvalExtraData;

                    JS_AddValueRoot(cx, valArr);
                    JS_CallFunctionValue(cx, thisObj, jsvalCallback, 2, valArr, &retval);
                    JS_RemoveValueRoot(cx, valArr);
                }
                else
                {
                    jsval senderVal = OBJECT_TO_JSVAL(proxy->obj);
                    JS_AddValueRoot(cx, &senderVal);
                    JS_CallFunctionValue(cx, thisObj, jsvalCallback, 1, &senderVal, &retval);
                    JS_RemoveValueRoot(cx, &senderVal);
                }
            }

            // I think the JSCallFuncWrapper isn't needed.
            // Since an action will be run by a cc.Node, it will be released at the Node::cleanup.
            // By James Chen
            // JSCallFuncWrapper::setTargetForNativeNode(node, (JSCallFuncWrapper *)this);
        });

        js_proxy_t *proxy = js_get_or_create_proxy<cocos2d::CallFunc>(cx, ret);
        JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(proxy->obj));

        JS_SetReservedSlot(proxy->obj, 0, argv[0]);
        if(argc > 1) {
            JS_SetReservedSlot(proxy->obj, 1, argv[1]);
        }
//        if(argc == 3) {
//            JS_SetReservedSlot(proxy->obj, 2, argv[2]);
//        }

      //  test->execute();
        return JS_TRUE;
    }
    JS_ReportError(cx, "Invalid number of arguments");
    return JS_FALSE;
}

這是 JS 層調用 cc.CallFunc.create 時,底層執(zhí)行的 C++ 函數(shù),這里面用了一些 C++11 的特性,包括 std::shared_ptr 智能指針和 lambda 表達式(也很簡單,不熟悉的童鞋可以自己找資料熟悉下)。

這里面回調函數(shù)被封裝到了lambda表達式里面,通過=方式引用外部的 tmpCobj 變量,這種方式跟 JS 的閉包非常類似。依然使用 JS_CallFunctionValue 進行函數(shù)調用。注意,這種調用方式跟 JS 里面的 apply 方式是很類似的。

這里面有一對函數(shù)非常有趣,JS_AddValueRoot 和JS_RemoveValueRoot,這兩個函數(shù) JS_CallFunctionValue 調用包起來了。因為這個 valArr 或 senderVal 是在棧上臨時生成的,沒有指定對應的 root。但是中間又進行了 JS 函數(shù)的調用,所以這兩個值可能在 JS 函數(shù)調用的時候被 SpiderMonkey 虛擬機給垃圾回收掉(可以去看看 JS 的垃圾回收機制原理)。于是我們需要給他們掛一個 root,保護一下,不被回收掉。

二、調用代碼

先看一下構造函數(shù)

CallFuncN * CallFuncN::create(const std::function<void(Node*)> &func)
{
    auto ret = new CallFuncN();
    if (ret && ret->initWithFunction(func) ) {
        ret->autorelease();
        return ret;
    }
    CC_SAFE_DELETE(ret);
    return nullptr;
}
bool CallFuncN::initWithFunction(const std::function<void (Node *)> &func)
{
    _functionN = func;
    return true;
}

傳進來的 lambda 表達式被存為一個 std::function<void(Node*)> 類型。

調用代碼異常簡單,使用 _functionN 進行調用即可。

void CallFuncN::execute() {
    if (_callFuncN) {
        (_selectorTarget->*_callFuncN)(_target);
    }
    else if (_functionN) {
        _functionN(_target);
    }
}

對比上一篇中的方式,我認為這種調用方式更加合理,因為這種調用方式,對 C++ 層 Core 代碼,隱藏了腳本機制。而之前的調用方式是顯示通過腳本引擎來調用的。 看完此篇和前篇,我們仔細分析了 Cocos2d-x JSB 里面的回調函數(shù)的寫法,詳細對于讀者而言自己實現(xiàn)一個回調函數(shù)已經(jīng)不是什么特別困難的事情。

在剛完成此篇的時候,突然發(fā)現(xiàn)有這么一個帖子,講的也是 JSB 回調函數(shù),寫得很不錯,還是 IAP 的,可以作為額外閱讀參考:

Cocos2d-x 使用 iOS 游戲內付費 IAP(JSB 篇)

還有一篇可以學習的:
JS 的回調函數(shù)的參數(shù)構造注記——Web 學習筆記(十八)

關于回調函數(shù)的問題,先說這些吧。

下篇繼續(xù),我們來討論一下注冊函數(shù)的事