动手点关注 干货不迷路 👆
背景
苹果的 Objective-C 编译器允许用户在同一个源文件里自由地混合使用 C++和 Objective-C,混编后的语言叫 Objective-C++。相对于其它语言(例如 Swift、Kotlin、Dart 等)和 C++的文件隔离和架桥通信(例如 Kotlin 使用JNI
,Dart 使用FFI
),Objective-C 和 C++的同文件混编方式无疑是令人舒畅的。OC/C++
混编虽然可以在一个文件中进行编写,但是有一些注意事项需要了解:Objective-C++没有为 OC 类增加 C++的功能,也没有为 C++增加 OC 的功能,例如:不能用 OC 语法调用 C++对象,也不能为 OC 对象增加构造函数和析构函数,也不能将this
和self
互相替换使用。类的体系结构是独立的,C++类不能继承 OC 类,OC 类也不能继承 C++类。
本文主要就之前令人困惑的 OC 的Block
和 C++的lambda
混编问题做一些探索。
实验环境:C++版本为 C++14,OC 只局限于 ARC。
基本了解
在深入探索之前,先通过对比的方式了解下二者:
语法
^(int x, NSString *y){} // ObjC, take int and NSString*
[](int x, std::string y){} // C++, take int and std::string
^{ return 42; } // ObjC, returns int
[]{ return 42; } // C++, returns int
^int { if(something) return 42; else return 43; }
[]()->int { if(something) return 42; else return 43; }
原理
OC 的Block
的底层可以参考《深入研究 Block 捕获外部变量和 __block 实现原理》(https://halfrost.com/ios_block/),这里不做深入探究,仅仅是要展开代码达到对比效果。
- (void)viewDidLoad {
[super viewDidLoad];
int x = 3;
void(^block)(int) = ^(int a) {
NSLog(@"%d", x);
};
block(5);
}
通过clang -rewrite-objc
重写,可以得到以下结果:
struct __ViewController__viewDidLoad_block_impl_0 {
struct __block_impl impl;
struct __ViewController__viewDidLoad_block_desc_0* Desc;
int x;
__ViewController__viewDidLoad_block_impl_0(void *fp, struct __ViewController__viewDidLoad_block_desc_0 *desc, int _x, int flags=0) : x(_x) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __ViewController__viewDidLoad_block_func_0(struct __ViewController__viewDidLoad_block_impl_0 *__cself, int a) {
int x = __cself->x; // bound by copy
NSLog((NSString *)&__NSConstantStringImpl__var_folders_st_jhg68rvj7sj064ft0rznckfh0000gn_T_ViewController_d02516_mii_0, x);
}
static struct __ViewController__viewDidLoad_block_desc_0 {
size_t reserved;
size_t Block_size;
} __ViewController__viewDidLoad_block_desc_0_DATA = { 0, sizeof(struct __ViewController__viewDidLoad_block_impl_0)};
static void _I_ViewController_viewDidLoad(ViewController * self, SEL _cmd) {
((void (*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("ViewController"))}, sel_registerName("viewDidLoad"));
int x = 3;
void(*block)(int) = ((void (*)(int))&__ViewController__viewDidLoad_block_impl_0((void *)__ViewController__viewDidLoad_block_func_0, &__ViewController__viewDidLoad_block_desc_0_DATA, x));
((void (*)(__block_impl *, int))((__block_impl *)block)->FuncPtr)((__block_impl *)block, 5);
}
而 C++ lambda
采取了截然不同的的实现机制,会把lambda
表达式转换为一个匿名 C++类。这里借助cppinsights
看下 C++ lambda
的实现。
#include <cstdio>
struct A {
int x;
int y;
};
int main()
{
A a = {1, 2};
int m = 3;
auto add = [&a, m](int n)->int {
return m + n + a.x + a.y;
};
m = 30;
add(20);
}
#include <cstdio>
struct A
{
int x;
int y;
};
int main()
{
A a = {1, 2};
int m = 3;
class __lambda_12_15
{
public:
inline int operator()(int n) const
{
return ((m + n) + a.x) + a.y;
}
private:
A & a;
int m;
public:
__lambda_12_15(A & _a, int & _m)
: a{_a}
, m{_m}
{}
};
__lambda_12_15 add = __lambda_12_15{a, m};
m = 30;
add.operator()(20);
return 0;
}
可以看到:lambda
表达式add
被转换为类__lambda_12_15
,且重载了操作符()