深入理解 OC/C++ 闭包

动手点关注 干货不迷路 👆

背景

苹果的 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 对象增加构造函数和析构函数,也不能将thisself互相替换使用。类的体系结构是独立的,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,且重载了操作符()

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值