【cocos2d-x从c++到js】13:回调函数2——JSCallbackWrapper

简介:

上一篇我们讲了按键回调,这一次我们来说说各种逻辑上的回调函数。


Cocos2d-x里面一共有三大类回调函数,第一是按键回调CCMenu相关的,第二类是定时器相关的回调

Schedule,第三类是Action相关的回调CallFunc。这些回调从最初的引擎版本中就存在着,一直到现在。


一、绑定代码


在JSB的解决方案中,对于后两类函数,引擎统一封装成JSCallbackWrapper及其子类。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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回调函数的包装器。三个接口也一目了然,回调函数,this,外部数据。


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
// 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时,底层执行的C++函数,这里面用了一些C++11的特性,包括std::shared_ptr智能指针和lambda表达式(也很简单,不熟悉的童鞋可以自己找资料熟悉下)。


这里面回调函数被封装到了lambda表达式里面,通过=方式引用外部的tmpCobj变量,这种方式跟JS的闭包非常类似。依然使用JS_CallFunctionValue进行函数调用。注意,这种调用方式跟JS里面的apply方式是很类似的。


这里面有一对函数非常有趣,JS_AddValueRoot和JS_RemoveValueRoot,这两个函数JS_CallFunctionValue调用包起来了。因为这个valArr或senderVal是在栈上临时生成的,没有指定对应的root。但是中间又进行了JS函数的调用,所以这两个值可能在JS函数调用的时候被SpiderMonkey虚拟机给垃圾回收掉(可以去看看JS的垃圾回收机制原理)。于是我们需要给他们挂一个root,保护一下,不被回收掉。


二、调用代码


先看一下构造函数

1
2
3
4
5
6
7
8
9
10
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;
}

1
2
3
4
5
bool  CallFuncN::initWithFunction( const  std::function< void  (Node *)> &func)
{
     _functionN = func;
     return  true ;
}


传进来的lambda表达式被存为一个std::function<void(Node*)>类型。


调用代码异常简单,使用_functionN进行调用即可。

1
2
3
4
5
6
7
8
void  CallFuncN::execute() {
     if  (_callFuncN) {
         (_selectorTarget->*_callFuncN)(_target);
     }
     else  if  (_functionN) {
         _functionN(_target);
     }
}



对比上一篇中的方式,我认为这种调用方式更加合理,因为这种调用方式,对C++层Core代码,隐藏了脚本机制。而之前的调用方式是显示通过脚本引擎来调用的。

看完此篇和前篇,我们仔细分析了Cocos2d-x JSB里面的回调函数的写法,详细对于读者而言自己实现一个回调函数已经不是什么特别困难的事情。


在刚完成此篇的时候,突然发现有这么一个帖子,讲的也是JSB回调函数,写得很不错,还是IAP的,可以作为额外阅读参考:

Cocos2d-x使用iOS游戏内付费IAP(JSB篇)


还有一篇可以学习的:

JS的回调函数的参数构造注记——Web学习笔记(十八)


关于回调函数的问题,先说这些吧。


下篇继续,我们来讨论一下注册函数的事


 本文转自 老G 51CTO博客,原文链接:http://blog.51cto.com/goldlion/1354903,如需转载请自行联系原作者





相关文章
|
4月前
|
JavaScript 前端开发 Serverless
函数计算只支持Node.js,我用C++写的程序怎么运行?
函数计算只支持Node.js,我用C++写的程序怎么运行?
91 1
|
30天前
|
存储 JSON 安全
【C++ JSON库 json值的创建手段】深入探究C++中JSON对象定位与操作:从引用到回调函数
【C++ JSON库 json值的创建手段】深入探究C++中JSON对象定位与操作:从引用到回调函数
64 0
|
22天前
|
人工智能 机器人 中间件
【C++】C++回调函数基本用法(详细讲解)
【C++】C++回调函数基本用法(详细讲解)
|
27天前
|
JavaScript
JS回调函数
JS回调函数
9 0
|
1月前
|
存储 JavaScript 前端开发
js开发:请解释什么是回调函数(callback function),并给出一个示例。
回调函数是JavaScript中处理异步编程的一种常见模式,常用于事件驱动和I/O操作。它们作为参数传递给其他函数,在特定条件满足或任务完成后被调用。例如,`asyncOperation`函数接受回调函数`handleResult`,在模拟的异步操作完成后,调用`handleResult`并传递结果。这使得程序员能在操作完成后执行后续任务。
21 1
|
1月前
|
JavaScript 前端开发 Java
Java Script中的回调函数,可以用来做什么
Java Script中的回调函数,可以用来做什么
10 0
|
1月前
|
JavaScript 前端开发 UED
解释 JavaScript 中的异步编程和回调函数。
解释 JavaScript 中的异步编程和回调函数。
16 0
|
3月前
|
C++
c++将一个类的回调函数注入到另一个类中的方法
c++将一个类的回调函数注入到另一个类中的方法
|
3月前
|
前端开发 JavaScript 数据处理
【面试题】 javaScript 进阶之路 --- 《加深理解回调函数》
【面试题】 javaScript 进阶之路 --- 《加深理解回调函数》
|
3月前
|
JavaScript 前端开发
Node.js 回调函数的原理、使用方法
Node.js 回调函数的原理、使用方法
39 1