博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
【cocos2d-x从c++到js】回调函数2——JSCallbackWrapper
阅读量:1969 次
发布时间:2019-04-27

本文共 4255 字,大约阅读时间需要 14 分钟。

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


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的,可以作为额外阅读参考:



还有一篇可以学习的:



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


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

转载地址:http://uzypf.baihongyu.com/

你可能感兴趣的文章
知识点记录-springboot静态资源映射路径
查看>>
微服务springcloud2系列篇-配置与注册nacos组件
查看>>
用户权限设计-基于RBAC模型
查看>>
微服务springcloud2系列篇-网关GATEWAY跨域问题
查看>>
微服务springcloud(H版本)与springboot(2.X版本) maven常见问题整理记录
查看>>
Java并发以及多线程基础
查看>>
软件质量的8个特性
查看>>
应届渣渣前端的艰难求职之路
查看>>
2021年不可错过的17种JS优化技巧(一)
查看>>
月薪15~20k的前端面试问什么?
查看>>
一文学会使用Vue3
查看>>
在 Vue 中用 Axios 异步请求API
查看>>
LVM逻辑卷------基础命令详解(三分钟入门)
查看>>
mysql——介绍及安装与基本用法
查看>>
MySQL数据库之索引
查看>>
MYSQL——事务操作+视图+存储引擎
查看>>
Mysql——完全备份+增量备份+备份恢复
查看>>
MySQL进阶查询(SELECT 语句高级用法)
查看>>
Mysql 之主从复制
查看>>
LVS负载均衡------NAT模式
查看>>