0%

Objective-C 匿名函数

Blocks是带有自动变量(局部变量) 的匿名函数

语法

1
2
3
^int (int val) {
// todo
}

可省略参数列表以及返回值

1
2
3
4
5
// 自动推导返回值类型为int
^(int val) { return val; }

// 省略参数列表
^ { printf("func"); }

Blocks变量的声明:将C语言函数指针声明中的*改成^

1
2
// 变量名字为blk
int (^blk)(int) = ^(int cnt) { return cnt+1; };

通过typedef简化函数定义

1
2
3
4
5
6
7
typedef int (^blk_t)(int);

void func(int (^blk)(int)) // 简化前
void func(blk_t blk) // 简化后

int (^func()(int)) // 简化前
blk_t func() // 简化后

变量的捕获

Block表达式截获定义时自动变量的值,值只读

1
2
3
4
int val = 10;
void (^blk)(void) = ^{ printf("%d", val); };
val = 11;
blk(); // print 10

添加__block声明,值可写

实现原理

例子

1
2
3
4
5
int main() {
void (^blk)(void) = ^{ print("Block\n"); };
blk();
return 0;
}

上面的Block由几个部分组成

  1. 函数体
    1
    2
    3
    static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
    printf("Block\n");
    }
    将Block内的函数体放到一个静态函数中
  2. 定制Block结构体
    1
    2
    3
    4
    5
    struct __main_block_impl_0 {
    struct __block_impl impl;
    struct __main_block_desc_0 *Desc;
    // 截获的变量...
    }
    分为以下两个部分
    1. 常规Block结构体
      1
      2
      3
      4
      5
      6
      struct __block_impl {
      void *isa;
      int Flags;
      int Reserved;
      void *FuncPtr; // 保存函数体指针
      };
      通用的Block结构,如isa,函数指针等
    2. 定制Block描述块
      1
      2
      3
      4
      struct __main_block_desc_0 {
      unsigned long reserved;
      unsigned long Block_size;
      }
      特定的属于该Block的描述结构,如
    3. Block描述块数据
      1
      2
      3
      static __main_block_desc_0 __main_block_desc_0_DATA = { 
      0, sizeof(struct __main_block_impl_0)
      };
    4. 截获的变量
      各种类型变量的存储情况:
      • 对于局部变量,值存储
      • 对于全局变量,不进行存储
      • 对于静态局部变量,存储指针

转换后的实现

1
2
3
4
5
6
struct __main_block_impl_0 blk = 
__main_block_impl_0(
__main_block_func_0,
&__main_block_desc_0_DATA
);
(*blk->impl.FuncPtr)(blk);

__block实现

例子

1
2
3
4
int main() {
__block int val = 10;
void (^blk) (void) = ^{ val = 1; };
}

__block int val = 10转换为

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
struct __Block_byref_val_0 {
void *__isa;
__Block_byref_val_0 *__forwarding; // 指向正确的__block变量(堆/栈)
int __flags;
int __size;
int val;
}

__Block_byref_val_0 val = {
0,
&val,
0,
sizeof(__Block_byref_val_0),
10
}

^{val = 1;}转换为

1
2
3
4
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
__Block_byref_val_0 *val = __cself->val;
(val->__forwarding->val) = 1;
}

本质上也是保存对象的指针,但是与静态局部变量不一样的是,这个指针并非存到__main_block_impl_0中,而是存放到__Block_byref_val_0中,这样目的是一个__block变量能被多个Block使用

Block存储域

  • 栈:isa_NSConcreteStackBlock
  • 堆:isa_NSConcreteMallocBlock
  • .data:isa_NSConcreteGlobalBlock

一般Block都存在栈上,存在.data段上的两种情况为:

  • Block在全局变量中声明
  • Block语法表达式中不截获自动变量
    1
    2
    3
    4
    5
    6
    7
    typedef int (^blk_t)(int);
    for (int rate = 0; rate < 10; ++rate) {
    // _NSConcreteStackBlock
    blk_t blk = ^(int count) { return rate*count; }
    // _NSConcreteGlobalBlock
    blk_t blk2 = ^(int count) { return count; }
    }

存在堆上面的情况为:
变量作用域结束

1
2
3
blk_t func(int rate) {
return ^(int cnt) { return rate*cnt; };
}

函数返回一个Block,Block在栈上面生成的。当ARC有效时,编译器会自动生成从栈上复制到堆上的代码

1
2
3
4
5
blk_t func(int rate) {
blk_t tmp = &__func_block_impl_0(__func_block_func_0, &__func_block_desc_0_DATA, rate);
tmp = objc_retainBlock(tmp);
return objc_autoreleaseReturnValue(tmp);
}

copy

使用[blk copy]的效果:

  • _NSConcreteStackBlock:从栈复制到堆
  • _NSConcreteMallocBlock:引用计数增加
  • _NSConcreteGlobalBlock:什么也不做

__block变量存储域

__block变量由使用它的Block管理


__block变量正确访问的原理:
使用__forwarding成员变量。__block变量发生复制行为时,正确修改__forwarding成员变量

截获对象

当截获的变量是对象时,存放到__main_block_impl_0时需要用__strong修饰

1
2
3
4
5
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0 *Desc;
id __strong array;
}

除此之外,还有一对copy/dispose函数,用于管理对象的引用计数。其中copy在Block从栈上复制到堆上调用;dispose在Block在堆上被废弃时调用

1
2
3
4
5
6
7
8
9
10
11
12
static void __main_block_copy_0(
struct __main_block_impl_0 *dst,
struct __main_block_impl_0 *src) {
_Block_object_assign(
&dst->val,
src->val,
BLOCK_FIELD_IS_BTREF);
}

static void __main_block_dipose_0(struct __main_block_impl_0 *src) {
_Block_object_dispose(src->val, BLOCK_FIELD_IS_BTREF);
}

Block从栈上复制到堆上的情况:

  • [blk copy]
  • Block作为函数返回值返回时
  • Block赋值给__strong修饰符id类型的类或者Block类型成员变脸时

__block对象

同样,存到__Block_byref_val_0时需要用__strong修饰

1
2
3
4
5
6
7
8
9
struct __Block_byref_val_0 {
void *__isa;
__Block_byref_val_0 *__forwarding; // 指向正确的__block变量(堆/栈)
int __flags;
int __size;
void (*__Block_byref_id_object_copy)(void*, void*);
void (*__Block_byref_id_object_dispose)(void*);
__strong id obj;
}

同样,也生成一对copy/dispose静态函数,作用相同