看 React Native 源码时发现里面很多实现都是通过 C++
实现,为了看懂它的实现,对 C++ 和 OC 的混编进行一番探索。
1. OC & C++ 混编
1.1 文件类型
- .h : 头文件,可以同时当做 OC 以及 C++ 的头文件
- .m : OC 的实现文件
- .hpp : C++ 的头文件
- .cpp : C++ 的实现文件
- .mm : OC & C++ 混编文件,该文件中可以在 OC 代码中使用 C++
1.2 一个简单混编代码
C++ 代码:
// ===================== Person.h =====================
// ===================== C++ 头文件 =====================
#ifndef Person_h
#define Person_h
#include <stdio.h>
#include <string.h>
#include <iostream>
#endif /* Person_h */
namespace cppdemo {
class Person {
public:
~Person();
int age;
void sayHello();
// 默认构造函数
Person();
// 带参数的构造函数
Person(int _age, std::string _name);
private:
std::string name;
};
}
// ===================== Person.cpp =====================
// ===================== C++ 实现文件 =====================
#include "Person.h"
namespace cppdemo {
// '::' 表示是 class Person 中的方法
// class Person 中的默认构造函数
Person::Person() {
this->age = 10;
this->name = "Tommy";
}
Person::Person(int _age, std::string _name) {
this->age = _age;
this->name = _name;
}
// 以上方法等同与下面这种实现
// Person::Person(int _age, std::string _name): age(_age), name(_name) {
//
// }
void Person::sayHello() {
std::cout << name << " hello age: " << age << "\n";
}
}
OC & C++ 混编代码:
// ===================== Family.h =====================
// ===================== OC 头文件 =====================
#import <Foundation/Foundation.h>
@interface Family : NSObject
- (void)addTommy;
- (void)addLily;
- (void)everyBodySayHello;
@end
// ===================== Family.mm =====================
// ===================== OC & C++ 混编文件 =====================
#import "Family.h"
#import "Person.h"
using namespace cppdemo;
@implementation Family {
cppdemo::Person *Tommy;
cppdemo::Person *Lily;
}
- (void)addTommy {
Tommy = new cppdemo::Person();
}
- (void)addLily {
int age = 22;
NSString *name = @"Lily";
Lily = new cppdemo::Person(age, [name UTF8String]);
}
- (void)everyBodySayHello {
Tommy->sayHello();
Lily->sayHello();
}
@end
以上就是一个在 OC 中使用 C++ 的简单实现,接下来就可以直接通过 OC 代码调用 C++ 文件中的方法、类等。可以通过以下代码测试一下
Family *family = [Family new];
[family addTommy];
[family addLily];
[family everyBodySayHello];
可以在控制台看到输出
Tommy hello age: 10
Lily hello age: 22
2. React Native 源码简单解读
为了更好的学习 C++ 在 iOS 中的使用,我针对 React Native 中的 RCTCxxBridge.mm
,Instance.cpp
,NativeToJsBridge.cpp
, JSCExecutor.cpp
文件简单解读。不对具体实现进行说明,只为了说明 RN 原生 调用 JS 的过程中如何进行 OC 和 C++ 的混编的。涉及到的一些 C++ 类型在下面 3. 一些 C++ 类型介绍
有说明,如 std::shared_ptr
,std::move
等
- 先不管前面 RN 都做了什么,我们以方法
- (void)executeSourceCode:(NSData *)sourceCode sync:(BOOL)sync
作为入口,接下来是一个 RN 原生 调用 JS 的过程
// RCTCxxBridge.mm
- (void)executeSourceCode:(NSData *)sourceCode sync:(BOOL)sync
{
// ......
#if RCT_DEV
if (self.devSettings.isHotLoadingAvailable && self.devSettings.isHotLoadingEnabled) {
// ....
[self enqueueJSCall:@"HMRClient"
method:@"enable"
args:@[@"ios", path, host, RCTNullIfNil(port)]
completion:NULL]; }
#endif
}
- 接下来调用了
enqueueJSCall
// RCTCxxBridge.mm
- (void)enqueueJSCall:(NSString *)module method:(NSString *)method args:(NSArray *)args completion:(dispatch_block_t)completion
{
//.....
if (strongSelf->_reactInstance) {
strongSelf->_reactInstance->callJSFunction([module UTF8String], [method UTF8String],
convertIdToFollyDynamic(args ?: @[]));
// .....
}
}
- 我们看以下
strongSelf->_reactInstance
的实现,可以看出它是一个 C++Instance
类,实现在文件Instance.cpp
中。这样就在 OC 中调用 C++ 的代码。代码中std::shared_ptr<Instance>
是声明类型用的,具体意思可以看下面3. 一些 C++ 类型介绍
// RCTCxxBridge.mm
@implementation RCTCxxBridge
{
// .......
// This is uniquely owned, but weak_ptr is used.
std::shared_ptr<Instance> _reactInstance;
}
- (void)start {
// ......
// This doesn't really do anything. The real work happens in initializeBridge.
_reactInstance.reset(new Instance);
// ......
}
- 接着上面 (2) 看
_reactInstance->callJSFunction
的实现,下面std::move
的说明也可以看3.3
// Instance.cpp
void Instance::callJSFunction(std::string &&module, std::string &&method,
folly::dynamic &¶ms) {
callback_->incrementPendingJSCalls();
nativeToJsBridge_->callFunction(std::move(module), std::move(method),
std::move(params));
}
- 接着调用了
nativeToJsBridge_->callFunction
方法
// NativeToJsBridge.cpp
void NativeToJsBridge::callFunction(
std::string&& module,
std::string&& method,
folly::dynamic&& arguments) {
// ......
runOnExecutorQueue([this, module = std::move(module), method = std::move(method), arguments = std::move(arguments), systraceCookie](JSExecutor* executor) {
// ......
// This is safe because we are running on the executor's thread: it won't
// destruct until after it's been unregistered (which we check above) and
// that will happen on this thread
executor->callFunction(module, method, arguments);
});
}
- 接下来调用的是
JSExecutor
的的callFunction
方法
// JSExecutor.cpp
void JSCExecutor::callFunction(
const std::string& moduleId,
const std::string& methodId,
const folly::dynamic& arguments) {
// ......
return m_callFunctionReturnFlushedQueueJS->callAsFunction(
{Value(m_context, String::createExpectingAscii(m_context, moduleId)),
Value(m_context, String::createExpectingAscii(m_context, methodId)),
Value::fromDynamic(m_context, std::move(arguments))});
// ......
}
- 接着调用到以下代码。在下面代码中已经可以看到了
JSValueRef
、JSObjectRef
,看到这些以Ref
结尾的类型是不是很熟悉。std::initializer_list
也在3.4
中有说明
// Value.cpp
Value Object::callAsFunction(std::initializer_list<JSValueRef> args) const {
return callAsFunction(nullptr, args.size(), args.begin());
}
// 这是一个箭头
// ||
// ||
// \ /
// \/
Value Object::callAsFunction(JSObjectRef thisObj, int nArgs, const JSValueRef args[]) const {
JSValueRef exn;
JSValueRef result = JSC_JSObjectCallAsFunction(m_context, m_obj, thisObj, nArgs, args, &exn);
// ......
}
JSC_JSObjectCallAsFunction
方法走到最后最后,又走到了我们JavaScriptCore.framework
中了
// <JavaScriptCore/JSObjectRef>
/*!
@function
@abstract Calls an object as a function.
@param ctx The execution context to use.
@param object The JSObject to call as a function.
@param thisObject The object to use as "this," or NULL to use the global object as "this."
@param argumentCount An integer count of the number of arguments in arguments.
@param arguments A JSValue array of arguments to pass to the function. Pass NULL if argumentCount is 0.
@param exception A pointer to a JSValueRef in which to store an exception, if any. Pass NULL if you do not care to store an exception.
@result The JSValue that results from calling object as a function, or NULL if an exception is thrown or object is not a function.
*/
JS_EXPORT JSValueRef JSObjectCallAsFunction(JSContextRef ctx, JSObjectRef object, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception);
看 <JavaScriptCore/JavaScriptCore.h>
中头文件的定义,可以发现 JavaScriptCore.framework
提供了 C 语言的接口,同时也提供了 OC 的接口。
// <JavaScriptCore/JavaScriptCore.h
#ifndef JavaScriptCore_h
#define JavaScriptCore_h
// 底层 C 语言接口
#include <JavaScriptCore/JavaScript.h>
#include <JavaScriptCore/JSStringRefCF.h>
#if defined(__OBJC__) && JSC_OBJC_API_ENABLED
// OC 对底层 C 语言接口的再次封装
#import "JSContext.h"
#import "JSValue.h"
#import "JSManagedValue.h"
#import "JSVirtualMachine.h"
#import "JSExport.h"
#endif
#endif /* JavaScriptCore_h */
3. 一些 C++ 类型介绍
以下 C++ 类型介绍来自 cppreference
3.1 shared_ptr
std::shared_ptr
是通过指针保持对象共享所有权的智能指针。多个 shared_ptr
对象可占有同一对象。下列情况之一出现时销毁对象并解分配其内存:
用 delete
表达式或在构造期间提供给 shared_ptr
的定制删除器销毁对象。
struct Derived: public Base
{
Derived() { std::cout << " Derived::Derived()\n"; }
~Derived() { std::cout << " Derived::~Derived()\n"; }
};
int main()
{
std::shared_ptr<Base> p = std::make_shared<Derived>();
p.reset(); // 从 main 释放所有权
}
3.2 unique_ptr
std::unique_ptr
是通过指针占有并管理另一对象,并在 unique_ptr
离开作用域时释放该对象的智能指针。
在下列两者之一发生时用关联的删除器释放对象:
通过调用 get_deleter()(ptr) ,用潜在为用户提供的删除器释放对象。默认删除器用 delete 运算符,它销毁对象并解分配内存。
struct D
{
D() { std::cout << "D::D\n"; }
~D() { std::cout << "D::~D\n"; }
void bar() override { std::cout << "D::bar\n"; }
};
// 消费 unique_ptr 的函数能以值或以右值引用接收它
std::unique_ptr<D> pass_through(std::unique_ptr<D> p)
{
p->bar();
return p;
}
int main()
{
std::cout << "unique ownership semantics demo\n";
{
auto p = std::make_unique<D>(); // p 是占有 D 的 unique_ptr
auto q = pass_through(std::move(p));
assert(!p); // 现在 p 不占有任何内容并保有空指针
q->bar(); // 而 q 占有 D 对象
} // ~D 调用于此
}
3.3 std::move
std::move
用于指示对象 t
可以“被移动”,即允许从 t
到另一对象的有效率的资源传递。
特别是, std::move
生成标识其参数 t
的亡值表达式。它准确地等价于到右值引用类型的 static_cast 。
int main()
{
std::string str = "Hello";
std::vector<std::string> v;
// 使用 push_back(const T&) 重载,
// 表示我们将带来复制 str 的成本
v.push_back(str);
// 使用右值引用 push_back(T&&) 重载,
// 表示不复制字符串;而是
// str 的内容被移动进 vector
// 这个开销比较低,但也意味着 str 现在可能为空。
v.push_back(std::move(str));
}
3.4 std::initializer_list
std::initializer_list<T>
类型对象是一个访问 const T
类型对象数组的轻量代理对象。
std::initializer_list
对象在这些时候自动构造:
- 用花括号初始化器列表列表初始化一个对象,其中对应构造函数接受一个
std::initializer_list
参数 - 以花括号初始化器列表为赋值的右运算数,或函数调用参数,而对应的赋值运算符/函数接受
std::initializer_list
参数 - 绑定花括号初始化器列表到
auto
,包括在范围 for 循环中
initializer_list 可由一对指针或指针与其长度实现。复制一个 std::initializer_list
不会复制其底层对象。
template <class T>
struct S {
std::vector<T> v;
S(std::initializer_list<T> l) : v(l) {
std::cout << "constructed with a " << l.size() << "-element list\n";
}
void append(std::initializer_list<T> l) {
v.insert(v.end(), l.begin(), l.end());
}
std::pair<const T*, std::size_t> c_arr() const {
return {&v[0], v.size()}; // 在 return 语句中复制列表初始化
// 这不使用 std::initializer_list
}
};
template <typename T>
void templated_fn(T) {}
int main()
{
S<int> s = {1, 2, 3, 4, 5}; // 复制初始化
s.append({6, 7, 8}); // 函数调用中的列表初始化
for (int x : {-1, -2, -3}) // auto 的规则令此带范围 for 工作
std::cout << x << ' ';
auto al = {10, 11, 12}; // auto 的特殊规则
}