看 Block 不再懵逼

Block 的语法

依稀记得自己第一眼看到 Block 的时候一脸懵逼的样子,接着是第二次,第三次,每次出现的样子怎么都不一样…… 。后来才发现,其实 Block 只有两种形式:

  • Block 表达式
  • Block 声明

Block表达式

先来看一个完整的 Block 表达式:

^void (int event) {
    printf("block statement : %d", event);
};
​```
对应的格式为:

`^` `返回值类型` `参数列表 ` `表达式`

返回值 紧跟 ` ^` 后面,最多只能有一个,可以为空,不需要括号包裹。

参数列表可以有多个,需要放在圆括号中。

表达式使用 {} 包裹,末尾需要有分号。

如果返回值类型为空(void)可以省略:

`^` `参数列表` `表达式`

`^ (int event) { printf("block statement : %d", event); };`

如果没有返回值也没有参数,则两者都可省略:

`^` `表达式`

`^{ printf("block statement : %d", event); };`

这已经是 Block 的最简表达式了。

#### Block 声明

Block 的类似于 C 语言函数的定义。与 函数的不同点在于 Block 可以使用定义在它外部的变量。

一般函数的类型声明形式为:

`int (*func)(int);`

而 Block 的声明形式为:

`int (^blk)(int);`

两种形式的不同之处在于将 `*` 改成 `^`,后面的 ` blk` 代表该 Block 表达式的名称。

Block 有以下几种使用场景:

- 自动(局部)变量
- 函数参数
- 静态变量
- 静态全局变量
- 全局变量

我们先来看一个完整的 Block:

```objective-c
int (^blk)(int) = ^int (int count) {
    return count + 1;
};
​```

等号左边为 Block 的**声明**,等号右边为 Block 的**表达式**。

Block 可以和普通变量一样进行赋值:

```objective-c
int (^blk)(int) = ^int (int count) {
        return count + 1;
    };
    
    int (^blk2)(int) = blk;
    printf("blk2: %d", blk2(4));
​```

Block 也可以作为函数的参数传递:

```objective-C
void addFunc(int (^blk)(int)) {
    int result = blk(3);
    printf("%d", result);
}
​```
Block 还可以使用返回值使用:

```objective-c
int (^func()) (int) {
    return ^int (int count) { return count + 1; };
}
​```
这里有点特殊的是 Block 作为返回值,整个表达式并没有写在函数名称的前面, 而是将 `func()` 函数名称放在了 `^` 的后面。但我们仍然可以看出其左边为 Block 的返回值,右边为 Block 的参数,在函数体内构造对应类型的 Block 表达式返回。

从上面来看 Block 书写有些复杂,不便于理解,我们可以像定义结构体那样,定义某种 Block 类型。如下就定义了一种传入 `int` 返回 `int` 类型的 Block:

```objective-c
typedef int (^blk_t) (int);

上述的表达式都可以简化为:

void func(blk_t blk) {}
blk_t func(){
   return ^int (int count) { return count + 1; };
}

这下看起来跟普通变量的使用没什么区别了。

截获自动变量

何谓自动变量?我们可以理解为就是普通的变量(好废话...)。

Block 的形式跟函数很相似,也被称为 「匿名变量」。不过它有一个函数没有的功能就是能够截获自动变量,即使用函数体外部的变量,比如:

int val = 1;
void (^blk)(int) = ^void (int a) {
    printf("%d\n", val + a);
};
        
val = 2;
        
blk(10);
​```

这段代码的执行结果为 `11`,而不是 `12`。是因为 Block 会截获上下文变量的瞬时值,而之后的改变对其截获的值没有影响。

但是当我们在 Block 体内试图修改变量 val 的时候编译器会报错:

Variable is not assignable (missing __block type specifier)


提示我们使用 `__block` 修饰符。

如果这样定义变量 `__block int val = 1;` val 就可以在 Block 内部修改。

那如果 Block 截获 Objective-C 对象会怎么样?

```objective-c
NSMutableArray *array = [NSMutableArray new];
void (^blk)(int) = ^void (int a) {
    [array addObject:[NSObject new]];
};
​```

我们向截获的 array 对象加入元素,这样写没有问题,而向截获的 array 赋值的时候则会产生编译错误。该源代码截获的是 NSMutableArray 对象,如果用 C 语言来描述,即是截获 NSMutableArray 类对象的结构体指针。在 Block 内使用该指针没有问题,而赋值修改指针就会产生编译错误。

Block 使用 C 语言数组是需要特别注意的。源代码如下:

```objective-c
const char text[] = "hello word";
void (^block)(void) = ^{
    printf("%c\n", text[1]);
};

Block 体内只是使用 C 语言的字符串字面量数组,而并没有修改自动变量,因此看似没有问题,但实际上会产生编译错误。

这是因为在 Block 中,截获自动变量的方法没有实现对 C 语言数组的截获(连续内存地址空间),而只能捕获普通栈上变量或者指针。

修改为如下形式就不会有问题:

const char *text = "hello word";
void (^block)(void) = ^{
    printf("%c\n", text[1]);
};

本文为第三次阅读《Objective-C高级编程iOS与OSX多线程和内存管理》片段笔记,每次看都有所收获,但唯有记笔记效果是最佳的。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,189评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,577评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,857评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,703评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,705评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,620评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,995评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,656评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,898评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,639评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,720评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,395评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,982评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,953评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,195评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,907评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,472评论 2 342

推荐阅读更多精彩内容