笔记:Objective-C高级编程 第二章 Blocks

t
1862021-459edd5ef79e8373.jpg

第二章 Blocks

2.1 Blocks 概要

2.1.1 什么是Blocks

Blocks是C语言的扩充功能,带有自动变量(局部变量)的匿名函数。
C语言的函数中可能使用的变量

  • 自动变量(局部变量)
  • 函数的参数
  • 静态变量(静态局部变量)
  • 静态全局变量
  • 全局变量
    其中,在函数的多次调用之间能够传递值的变量有:
  • 静态变量(静态局部变量)
  • 静态全局变量
  • 全局变量

2.2 Blocks 模式

2.2.1 Blocks语法

完整形式的Block语法与一般的C语言函数定义相比,仅有二点不同:

  • 没有函数名
  • 带有^(插入记号 caret)

完整形式:
^int (int count){ return count+1;}
省略返回值类型
^(int count){ return count+1;}
省略返回值和参数类型
^{printf("Block");}

声明Block类型变量的示例:
int (^blk) (int);
Block类型变量的用途与C语言的变量作用完全相同

Block赋值:
int (^blk) (int) = ^(int count){return count+1;};
int (^blk1) (int) = blk;
int (^blk2) (int);
blk2 = blk1;
在函数参数中使用Block类型量可以向函数传递Block:
void func(int (^blk) (int))
{
}
在函数返回值中指定Block类型,可以将Block作为函数的返回值返回:

在函数参数和返回值中使用Block类型变量时 记述方式极为复杂,可以用命typedef来解决问题:
typedef int (^blk_t) (int);

Block的指针类型变量:
typedef int (^blk_t)(int);
blk_t blk = ^(int count) {return count+1;};
blk_t *blkptr = &blk;
(*blkptr)(10);

2.2.3 截获自动变量值

"带有自动变量值"在Blocks中表现为“截获自动变量值”,即保存该自动变量的瞬间值。

2.2.4 __block说明符

若想在Block语法的表达式中将值赋给在Block语法外声明的自动变量,需要在该自动变量上附加__block说明符
__block int val = 0; //该变量称为__block变量

2.2.5 截获的自动变量

int val = 0;
void (^blk) (void) = ^{val = 1;};//会产生编译错误

截获Objective-C对象:
id array = [[NSMutableArray alloc] init];
void (^blk)(void) = ^{
id obj = [[NSObject alloc] init];
[array addObject:obj];//不会报错
array = [[NSMutableArray alloc] init];//报错
}
在使用C语言数组时必须小心使用其指针:
const char text[] = "hello";//区别
void (^block)(void) = ^{
printf("%c\n",text[2]);//报错
};

const char *text = "hello";//区别
void (^block)(void) = ^{
printf("%c\n",text[2]);//不会报错
};

2.3 Block的实现

2.3.1 Block的实质

所谓Block就是Objective_C对象
struct __main_block_impl_0 *__cself;
参数__cself是__main_block_impl_0结构体的指针
objc_object与obj_class结构体相同
objc_object与obj_class结构体归根结底是在各个对象和类的实现中使用的最基本的结构体
struct __main_block_impl_0{
struct __block_impl impl;
struct __main_block_desc_0* Desc;
};
struct __block_impl{
void *isa;
int Flags;//标志
int Reserved;//今后版本升级所需的区域以及函数指针
void *FuncPtr;;//函数指针
};
struct __main_block_desc_0{
unsigned long reserved;
unsigned long Block_size;
};
typedef struct objc_object{
Class isa;
}*id;
typedef struct objc_class *Class;
struct objc_class{
Class isa;
};
struct class_t{
struct class_t *isa;
struct class_t *superclass;
Cache cache;
IMP *vtable;
uintptr_t date_NEVER_USE;
};
各类的结构体就是基于objc_class结构体的class_t结构体。

^{printf("Block")};
static void __main_block_func_0(struct __main_block_impl_0 *__cself)
{
printf("Block");
}
根据Block语法所属的函数名(此处为main)和该Block语法在该函数出现的顺序值(此处为0)来给经clang变换的函数换名。

2.3.2 截获自动变量值

Block语法表达式中使用的自动变量被作为成员变量追加到了__main_block_impl_0结构体中
Blocks的自动变量截获只针对Block中使用的自动变量
Block语法表达式所使用的自动变量值被保存到Block的结构体实例中

2.3.3 __block存储域类说明符

C语言存储域类说明符

  • typedef
  • extern
  • static//表示作为静态变量存储在数据区中
  • auto //表示作为自动变量存储在栈中
  • register
    struct __Block_byref_val_0{
    void *__isa;
    __Block_byref_val_0 *__forwarding;
    int __flags;
    int __size;
    int val;
    }

__block变量也同Block一样变成_Block_byref_val_0结构体类型的自动变量,即栈上生成的__Block_byref_val_0结构体实例

在Block中向静态变量赋值时 使用了指向该静态变量的指针。而向__block变量赋值要比这个更为复杂。Block的__main_block_impl_0结构体实例持有指向_block变量的_Block_byref_val_0结构体实例的指针。
_Block_byref_val_0结构体实例的成员变量_forwarding持有指向该实例自身的指针,通过成员变量__forwarding访问成员变量val
__block变量的_Block_byref_val_0结构体并不在Block用__main_block_impl_0结构体中 而是使用__Block_byref_val_0结构体实例的指针,这样做是为了在多个Block中使用__block变量

2.3.4 Block存储域

Block与__block变量的实质

名称 实质
Block 栈上Block的结构体实例
__block变量 栈上__block变量的结构体实例

Block的类

设置对象的存储域
_NSConcreteStackBlock
_NSConcreteGlobalBlock 程序的数据区域(.data区)
_NSConcreteMallocBlock

另还有一个程序区域(.text区)

在以下情况下,Block为_NSConcreteGlobalBlock类对象

  • 记述全局变量的地方有Block语法时
  • Block语法的表达式中不使用截获的自动变量
    除此之外的Block语法生成的Block为_NSConcreteStackBlock类对象 且设置在栈上。

Block超出变量作用域可存在的原因:
Block提供了将Block和__block变量从栈上复制到堆上的方法来解决这个问题。

编译器不能进行判断 将Block从栈上复制到堆上, 所以需使用copy方法:

  • 向方法或函数的参数中传递Block时

编译器可以自行判断 自动生成将Block从栈上复制到堆上的代码:

  • Block作为函数返回值时
  • Cocoa框架的方法且方法名中含有usingBlock等时
  • Grand Central Dispatch的API
    注:从栈上复制到堆上是相当消耗CPU的

2.3.5 __block变量存储域

__block变量用结构体成员变量__forwarding存在的原因:
通过Block的复制__block变量也从栈复制到堆,此时可同时访问栈上的__block变量和堆上的__block变量。
栈上的__block变量用结构体实例在__block变量从栈复制到堆上时,会将成员变量__forwarding的值替换为复制目标堆上的__block变量用结构体实例的地址。

2.3.6 截获对象

C语言结构体不能含有附有__strong修饰符的变量,因为编译器不知道应何时进行C语言结构体的初始化和废弃操作 不能很好地管理内存。
但是Object_C的运行库能够准确把握Block从栈复制到堆以及堆上的Block被废弃的时机。
什么时候栈上的Block会复制到堆呢?

  • 调用Block的copy实例方法
  • Block作为函数返回值返回时
  • 将Block赋值给附有__strong修饰符id类型的类或Block类型成员变量时
  • 在方法名中含有usingBlock的Cocoa框架方法或Grand Central Dispatch的API中传递Block时

Block中使用对象类型自动变量时,除以下情形外,推荐调用Block的copy实例方法:

  • Block作为函数返回值返回时
  • 将Block赋值给附有__strong修饰符id类型的类或Block类型成员变量时
  • 在方法名中含有usingBlock的Cocoa框架方法或Grand Central Dispatch的API中传递Block时

2.3.7 __block变量和对象

没有设定__autoreleasing修饰符与Block同时使用的方法:
__block id __autoreleasing obj = [[NSObject alloc] init];//编译器报错

2.3.7 循环引用

避免循环引用二种方式:

  • 使用__block
  • 使用__weak修饰符及__unsafe_unretained修饰符

使用_block变量的优点:

  • 通过__block变量可控制对象的持有期间
  • 在不能使用__weak修饰符的环境中可以不需使用__unsafe_unretained修饰符即可(不必担心悬垂指针)
  • 在执行Block时可动态地决定是否将nil或其它对象赋值在__block变量中

使用__block变量的缺点如下:

  • 为了避免循环引用必须执行Block

存在执行了Block语法 却不执行Block的路径时,无法避免循环引用.

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念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

推荐阅读更多精彩内容