V8 学习手册(四)—— 关联C++类

本文介绍了如何在V8引擎中通过ObjectTemplate和FunctionTemplate将C++类与JavaScript对象关联起来。通过SetInternalFieldCount和SetInternalField方法传递C++对象实例指针,并探讨了动态创建对象及实例化过程。虽然能将Objective-C类指针传入,但内存管理可能出现问题,目前采用的是内存泄露的临时解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

除了上文说的全局函数,我们还可以创建类函数。

虽然我使用JS很少,但是就我感觉,函数和对象其实就是一个东西的两种表现。函数是一种特殊的对象,创建的时候,需要进行一些操作,返回的值不是函数对象,而是指定的其他东西。


希望动态创建一个对象,自然首先想到应该使用ObjectTemplate。同时,也可以发现这个类中有SetInternalFieldCount,而Object中有SetInternalField。这两个方法,就用来传递C++代码中的对象实例指针给一个JS的Object。然后再将一个FunctionTemplate添加进入ObjectTemplate。这个FunctionTemplate对应的C函数,从传入的参数中取得前面传递的对象,然后进行对应操作。其实V8仅仅维护的是一个指针,所以我传入Objective-C的类指针,访问也没什么问题。不过引用计数管理内存应该会有问题吧。暂时还找不到应该何时去调用release,就先泄露着吧。

   

    Handle<ObjectTemplate> mars_obj_templ = ObjectTemplate::New();
    mars_obj_templ->Set("backToMars", FunctionTemplate::New(backToMars));
    mars_obj_templ->SetInternalFieldCount(1);


    Local<Object> obj = mars_obj_templ->NewInstance();
    V8Mars* mars = [[V8Mars alloc] init];
    mars.name = @"earth";
    obj->SetInternalField(0, External::New(mars));
    context->Global()->Set(String::New("mars"), obj);

    Handle<String> source = String::New("mars.backToMars();");

实现一下函数

Handle<Value> backToMars(const Arguments& args)
{
    Local<Object> self = args.Holder();
    Local<External> wrap = Local<External>::Cast(self->GetInternalField(0));
    void* ptr = wrap->Value();
    return String::New([static_cast<V8Mars*>(ptr).name cStringUsingEncoding:NSUTF8StringEncoding]);
}

这样确实我把一个对象实例同js的对象关联起来了。但是,假如我想要直接在js中创建一个新的实例,这个实例应该怎样和一个C的类实例关联呢?ObjectTemplate中有一个私有的New方法,可以传递一个构造函数,但是显然,我们不能使用。

还记得本小节开始的话么?对,这时候需要使用一个FunctionTemplate。用FunctionObject来产生一个原型Object。(话说,这个对于我还是很缠绕啊,JS本身是否就是这么设定的?)FunctionTemplate可以获取到自己对应的PrototypeTemplate,是一个ObjectTemplate。

    Handle<FunctionTemplate> mars_obj_templ = FunctionTemplate::New(createMars);
    Handle<ObjectTemplate> mars_obj_proto = mars_obj_templ->PrototypeTemplate();
    mars_obj_proto->Set("backToMars", FunctionTemplate::New(backToMars));
    
    Handle<ObjectTemplate> mars_obj_inst = mars_obj_templ->InstanceTemplate();
    mars_obj_inst->SetInternalFieldCount(1);
    
    Handle<Function> mars_obj_ctor = mars_obj_templ->GetFunction();
    context->Global()->Set(String::New("Mars"), mars_obj_ctor);
    
    Handle<String> source = String::New("var mars = new Mars(); mars.backToMars();");
    

我试图调整一下proto设置类函数的代码位置,也就是第三行的代码到后面去,结果就显示创建的对象中没有backToMars这个方法。我猜测,一旦对一个原型进行了修改,就会产生一个新的原型,而以前的方法,就是obj_templ->GetFunction的到的函数是无法访问新产生的原型。

这样,总算是可以了。贴上这次的代码


@interface V8Mars : NSObject {
@private
    NSString *name;
}
@property (nonatomic, retain)NSString *name;
@end 

@implementation V8Mars
@synthesize name;

- (void)dealloc
{
    [self.name release];
    [super dealloc];
}

- (id)init
{
    self = [super init];
    if (self != nil)
    {
        self.name = @"ground";
    }
    return self;
}

@end


Handle<Value> backToMars(const Arguments& args)
{
    Local<Object> self = args.Holder();
    Local<External> wrap = Local<External>::Cast(self->GetInternalField(0));
    void* ptr = wrap->Value();
    return String::New([static_cast<V8Mars*>(ptr).name cStringUsingEncoding:NSUTF8StringEncoding]);
}

Handle<Value> createMars(const Arguments& args)
{
    Local<Object> self = args.Holder();
    
    V8Mars* mars = [[V8Mars alloc] init];
    mars.name = @"Really mars";
    self->SetInternalField(0, External::New(mars));
    return self;
}


- (void)releateClass
{
    HandleScope handle_scope;
    Persistent<Context> context = Context::New();
    Context::Scope context_scope(context);
    
    Handle<FunctionTemplate> mars_obj_templ = FunctionTemplate::New(createMars);
    Handle<ObjectTemplate> mars_obj_proto = mars_obj_templ->PrototypeTemplate();
    mars_obj_proto->Set("backToMars", FunctionTemplate::New(backToMars));
    
    Handle<ObjectTemplate> mars_obj_inst = mars_obj_templ->InstanceTemplate();
    mars_obj_inst->SetInternalFieldCount(1);
    
    Handle<Function> mars_obj_ctor = mars_obj_templ->GetFunction();
    context->Global()->Set(String::New("Mars"), mars_obj_ctor);
    
    Handle<String> source = String::New("var mars = new Mars(); mars.backToMars();");
    Handle<Script> script = Script::Compile(source);
    Handle<Value> result = script->Run();
    
    String::Utf8Value utf8Result(result);
    NSLog(@"%@", [NSString stringWithCString:*utf8Result encoding:NSUTF8StringEncoding]);
    
    context.Dispose();
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值