Objective-C block实现代码分析

block内部结构

让我们写一个block

void exampleBlock() {
    // NSConcreteStackBlock
    int a = 1;
    __block int b = 2;
    int(^blockTest0)(int c) = ^(int c){
        return a + b + c;
    };
    int c = 3;
    blockTest0(c);

    // NSConcreteGlobalBlock
    void(^blockTest2)(void) = ^(void){
        ;
    };
    blockTest2();
}

用clang转成c分析下

clang -rewrite-objc block.c

能够看到他们的定义是

struct __exampleBlock_block_impl_0 {
  struct __block_impl impl;
  struct __exampleBlock_block_desc_0* Desc;
  int a;
  __Block_byref_b_0 *b; // by ref
  __exampleBlock_block_impl_0(void *fp, struct __exampleBlock_block_desc_0 *desc, int _a, __Block_byref_b_0 *_b, int flags=0) : a(_a), b(_b->__forwarding) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};

// __block_impl
struct __block_impl {
  void *isa;
  int Flags;
  int Reserved;
  void *FuncPtr;
};

// __exampleBlock_block_desc_0
struct __exampleBlock_block_impl_0 {
  struct __block_impl impl;
  struct __exampleBlock_block_desc_0* Desc;
  int a;
  __Block_byref_b_0 *b; // by ref
  __exampleBlock_block_impl_0(void *fp, struct __exampleBlock_block_desc_0 *desc, int _a, __Block_byref_b_0 *_b, int flags=0) : a(_a), b(_b->__forwarding) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};

// __exampleBlock_block_impl_2
struct __exampleBlock_block_impl_2 {
  struct __block_impl impl;
  struct __exampleBlock_block_desc_2* Desc;
  __exampleBlock_block_impl_2(void *fp, struct __exampleBlock_block_desc_2 *desc, int flags=0) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};

初始化和运行代码

void exampleBlock() {
    // blockTest0
    int a = 1;
    __attribute__((__blocks__(byref))) __Block_byref_b_0 b = {(void*)0,(__Block_byref_b_0 *)&b, 0, sizeof(__Block_byref_b_0), 2};

    int(*blockTest0)(int c) = (int (*)(int))&__exampleBlock_block_impl_0((void *)__exampleBlock_block_func_0, &__exampleBlock_block_desc_0_DATA, a, (__Block_byref_b_0 *)&b, 570425344);

    int c = 3;
    ((int (*)(__block_impl *, int))((__block_impl *)blockTest0)->FuncPtr)((__block_impl *)blockTest0, c);

    // blockTest2
    void(*blockTest2)(void) = (void (*)())&__exampleBlock_block_impl_2((void *)__exampleBlock_block_func_2, &__exampleBlock_block_desc_2_DATA);
    ((void (*)(__block_impl *))((__block_impl *)blockTest2)->FuncPtr)((__block_impl *)blockTest2);
}

我们先看看blockTest2,它是由 结构体impl, 结构体Desc, 构造方法__exampleBlock_block_impl_2() 组成展开后是

  • *isa 指向该实例对象(代码里是NSConcreteStackBlock,事实上应该是NSConcreteGlobalBlock)
  • Flags 用于按bit位表示一些block的附加信息
  • reserved 保留变量
  • *FuncPtr 函数指针,指向详细的block实现的函数调用地址(代码里是__exampleBlock_block_func_2)
static void __exampleBlock_block_func_2(struct __exampleBlock_block_impl_2 *__cself) {
        ;
}
  • size_t reserved 这个传进来的是0
  • Block_size 结构体的大小
static struct __exampleBlock_block_desc_2 {
  size_t reserved;
  size_t Block_size;
} __exampleBlock_block_desc_2_DATA = { 0, sizeof(struct __exampleBlock_block_impl_2)};


然后我们在看blockTest,它比blockTest2多了2个变量a, b

  • int a; 外部变量a,
  • Block_byref_b_0 *b; 加了block修饰的b, 传的是 Block_byref_b_0

在生成的初始化代码中则多了3个传入值

    int(*blockTest0)(int c) = (int (*)(int))&__exampleBlock_block_impl_0((void *)__exampleBlock_block_func_0, &__exampleBlock_block_desc_0_DATA, a, (__Block_byref_b_0 *)&b, 570425344);
  • a是这个是直接传值进去,然后被复制给 a
  • b是传的地址, 是把 __Block_byref_b_0 赋值给 b

__Block_byref_b_0这个结构体是

struct __Block_byref_b_0 {
  void *__isa;
__Block_byref_b_0 *__forwarding;
 int __flags;
 int __size;
 int b;
};

__forwarding 是一个指向自己的指针.

__Block_byref_b_0 的初始化代码例如以下:

__attribute__((__blocks__(byref))) __Block_byref_b_0 b = {(void*)0,(__Block_byref_b_0 *)&b, 0, sizeof(__Block_byref_b_0), 2};

我们能够看出a是直接复制进去的,b是被转到了一个结构体里,然后吧这个结构体的指针传进去,所以block不能改动a,能改动b.

  • 570425344 这个应该是传给Flags

blockTest0的Desc和blockTest2也有所不同,展开后是

static struct __exampleBlock_block_desc_0 {
  size_t reserved;
  size_t Block_size;
  void (*copy)(struct __exampleBlock_block_impl_0*, struct __exampleBlock_block_impl_0*);
  void (*dispose)(struct __exampleBlock_block_impl_0*);
} __exampleBlock_block_desc_0_DATA = { 0, sizeof(struct __exampleBlock_block_impl_0), __exampleBlock_block_copy_0, __exampleBlock_block_dispose_0};

多了2个函数指针copy, dispose,对于在调用前后改动对应变量的引用计数, 分别指向

static void __exampleBlock_block_copy_0(struct __exampleBlock_block_impl_0*dst, struct __exampleBlock_block_impl_0*src) {_Block_object_assign((void*)&dst->b, (void*)src->b, 8/*BLOCK_FIELD_IS_BYREF*/);}

static void __exampleBlock_block_dispose_0(struct __exampleBlock_block_impl_0*src) {_Block_object_dispose((void*)src->b, 8/*BLOCK_FIELD_IS_BYREF*/);}

再来看下blockTest0的*FuncPtr

 static int __exampleBlock_block_func_0(struct __exampleBlock_block_impl_0 *__cself, int c) {
  __Block_byref_b_0 *b = __cself->b; // bound by ref
  int a = __cself->a; // bound by copy

        return a + (b->__forwarding->b) + c;
    }

如可以看到的a使用输入的副本a, b使用一种结构,其中b

版权声明:本文博主原创文章。博客,未经同意不得转载。

时间: 2024-10-23 10:01:09

Objective-C block实现代码分析的相关文章

block实现代码分析

block内部结构 我们先写一个block void exampleBlock() { // NSConcreteStackBlock int a = 1; __block int b = 2; int(^blockTest0)(int c) = ^(int c){ return a + b + c; }; int c = 3; blockTest0(c); // NSConcreteGlobalBlock void(^blockTest2)(void) = ^(void){ ; }; bloc

MPC8313ERDB在Linux从NAND FLASH读取UBoot环境变量的代码分析

[email protected] 一.故事起因 因为文件系统的增大,已经大大的超出了8MB的NOR FLASH,而不得不把内核,文件系统和设备树文件保存到NAND FLASH上.但是因为使用的是RAMDISK,而无法保存一些个别的配置和参数,最简单的需要就是设置系统的IP了,,, 要使用统一的RAMDISK,而实现LINUX启动之后,设置成不能的参数功能,比较方便的就是从UBOOT把这些参数传递过去,这个得到了大家的认证,我们可以直接添加启动参数,然后在内核里面读出来,这种方法比较方法,唯一不

TS流之代码分析

代码分析前,先要了解TS流基本概念:TS流之基本概念. VLC解析TS流是通过libts库来分离的,libts库使用libdvbpsi库来解TS表. 1. libts库在加载的时候,会将以下如下两个函数注册下去,当接收到PAT或者PMT的时候,会进行调用.PAT和PMT每隔一段时间就会发送一次,以更新节目信息. static void PATCallBack( void*, dvbpsi_pat_t * ); static void PMTCallBack( void *data, dvbpsi

Linux -- 内存控制之oom killer机制及代码分析

近期,线上一些内存占用比較敏感的应用.在訪问峰值的时候,偶尔会被kill掉,导致服务重新启动.发现是Linux的out-of-memory kiiler的机制触发的. http://linux-mm.org/OOM_Killer oom kiiler会在内存紧张的时候,会依次kill内存占用较高的进程,发送Signal 15(SIGTERM).并在/var/log/message中进行记录.里面会记录一些如pid,process name.cpu mask,trace等信息,通过监控能够发现类似

Device Tree(三):代码分析【转】

转自:http://www.wowotech.net/linux_kenrel/dt-code-analysis.html Device Tree(三):代码分析 作者:linuxer 发布于:2014-6-6 16:03 分类:统一设备模型 一.前言 Device Tree总共有三篇,分别是: 1.为何要引入Device Tree,这个机制是用来解决什么问题的?(请参考引入Device Tree的原因) 2.Device Tree的基础概念(请参考DT基础概念) 3.ARM linux中和De

linux kernel的中断子系统之(七):GIC代码分析

一.前言 GIC(Generic Interrupt Controller)是ARM公司提供的一个通用的中断控制器,其architecture specification目前有四个版本,V1-V4(V2最多支持8个ARM core,V3/V4支持更多的ARM core,主要用于ARM64服务器系统结构).目前在ARM官方网站只能下载到Version 2的GIC architecture specification,因此,本文主要描述符合V2规范的GIC硬件及其驱动. 具体GIC硬件的实现形态有两

block的内存分析,循环引用,变量访问,数据结构定义

一.block的内存分析 如上图: 定义了一个weak的block,那么它在内存中的表现形式如右下角, 1.没有对block进行copy操作,而是weak,block就存储在栈空间中. 2.如果block存储于栈空间,不会对block内部所用到的对象产生强引用. 如上图: 对block进行了一次copy操作,如果对block进行copy操作,block就会存储到堆空间当中. 1.如果block存储于堆空间,就会对block内部所用到的对象产生强引用. 2.如下图所示,就会产生循环引用,造成P对象

[Asp.net 5] DependencyInjection项目代码分析4-微软的实现(4)

这个系列已经写了6篇,链接地址如下: [Asp.net 5] DependencyInjection项目代码分析 [Asp.net 5] DependencyInjection项目代码分析2-Autofac [Asp.net 5] DependencyInjection项目代码分析3-Ninject [Asp.net 5] DependencyInjection项目代码分析4-微软的实现(1) [Asp.net 5] DependencyInjection项目代码分析4-微软的实现(2) [As

【转】Device Tree(三):代码分析

原文网址:http://www.wowotech.net/linux_kenrel/dt-code-analysis.html 一.前言 Device Tree总共有三篇,分别是: 1.为何要引入Device Tree,这个机制是用来解决什么问题的?(请参考引入Device Tree的原因) 2.Device Tree的基础概念(请参考DT基础概念) 3.ARM linux中和Device Tree相关的代码分析(这是本文的主题) 本文主要内容是:以Device Tree相关的数据流分析为索引,