iOS Block
Posted xiaoxiaobukuang
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了iOS Block相关的知识,希望对你有一定的参考价值。
一、什么是Block
- Block是将
函数
及其执行上下文
封装起来的对象。 - Block调用即是
函数的调用
;
int multiplier = 6;io
int(^Block)(int) = ^int(int num)
return num*multiplier;
;
Block(2);
二、Block的本质
我们使用源码解析
- 使用
【clang -rewrite-objc file.m】
查看编译之后的文件内容;
文件XYBlock.m
中
- (void)method
int multiplier = 6;
int(^Block)(int) = ^int(int num)
return num*multiplier;
;
Block(2);
打开终端,进入XYBlock.m
所在的目录下,输入:
clang -rewrite-objc XYBlock.m
会在XYBlock.m
的目录下生成XYBlock.cpp
文件,打开该文件后解析如下:
结构体__XYBlock__method_block_impl_0
:
struct __XYBlock__method_block_impl_0
struct __block_impl impl;
struct __XYBlock__method_block_desc_0* Desc;
int multiplier;
__XYBlock__method_block_impl_0(void *fp, struct __XYBlock__method_block_desc_0 *desc, int _multiplier, int flags=0) : multiplier(_multiplier)
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
;
结构体__block_impl
:
struct __block_impl
void *isa;//isa指针,Block是对象的标志
int Flags;
int Reserved;
void *FuncPtr;//无类型的函数指针
;
函数__XYBlock__method_block_func_0
:
static int __XYBlock__method_block_func_0(struct __XYBlock__method_block_impl_0 *__cself, int num)
int multiplier = __cself->multiplier; // bound by copy
return num*multiplier;
三、截获变量
- (void)method
int multiplier = 6;
int(^Block)(int) = ^int(int num)
return num*multiplier;
;
multiplier = 4;
NSLog(@"result is %d",Block(2));
打印台:
result is 12
- 局部变量
1.基本数据类型
2.对象类型 - 静态局部变量
- 全局变量
- 静态全局变量
(1)、对于基本数据
类型的局部变量
截获其值。
(2)、对于对象
类型的局部变量连同所有权修饰符
一起截获。
(3)、对于局部静态变量以指针形式
截获。
(4)、对于全局变量、静态全局变量不截获
变量 | 是否捕获到block内部 | 访问法方式 |
---|---|---|
基本数据类型的局部变量 | 是 | 值传递 |
对象数据类型的局部变量 | 是 | 连同所有权修饰符截获 |
局部静态变量 | 是 | 指针传递 |
全局变量 | 否 | 直接传递 |
静态全局变量 | 否 | 直接传递 |
源码解析
- 使用
【clang -rewrite-objc -fobjc-arc file.m】
命令
文件XYBlock.m
如下:
通过编译生成XYBlock.cpp
文件内容如下:
结构体__XYBlock__method_block_impl_0
:
struct __XYBlock__method_block_impl_0
struct __block_impl impl;
struct __XYBlock__method_block_desc_0* Desc;
int var;
__unsafe_unretained id unsafe_obj;
__strong id strong_obj;
int *static_var;
__XYBlock__method_block_impl_0(void *fp, struct __XYBlock__method_block_desc_0 *desc, int _var, __unsafe_unretained id _unsafe_obj, __strong id _strong_obj, int *_static_var, int flags=0) : var(_var), unsafe_obj(_unsafe_obj), strong_obj(_strong_obj), static_var(_static_var)
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
;
函数__XYBlock__method_block_func_0
:
static void __XYBlock__method_block_func_0(struct __XYBlock__method_block_impl_0 *__cself)
int var = __cself->var; // bound by copy
__unsafe_unretained id unsafe_obj = __cself->unsafe_obj; // bound by copy
__strong id strong_obj = __cself->strong_obj; // bound by copy
int *static_var = __cself->static_var; // bound by copy
NSLog((NSString *)&__NSConstantStringImpl__var_folders_0k_f0kgkdz15jj50b2svwft4fzw0000gn_T_XYBlock_1271c0_mi_0, var);
NSLog((NSString *)&__NSConstantStringImpl__var_folders_0k_f0kgkdz15jj50b2svwft4fzw0000gn_T_XYBlock_1271c0_mi_1, unsafe_obj);
NSLog((NSString *)&__NSConstantStringImpl__var_folders_0k_f0kgkdz15jj50b2svwft4fzw0000gn_T_XYBlock_1271c0_mi_2, strong_obj);
NSLog((NSString *)&__NSConstantStringImpl__var_folders_0k_f0kgkdz15jj50b2svwft4fzw0000gn_T_XYBlock_1271c0_mi_3, (*static_var));
NSLog((NSString *)&__NSConstantStringImpl__var_folders_0k_f0kgkdz15jj50b2svwft4fzw0000gn_T_XYBlock_1271c0_mi_4, global_var);
NSLog((NSString *)&__NSConstantStringImpl__var_folders_0k_f0kgkdz15jj50b2svwft4fzw0000gn_T_XYBlock_1271c0_mi_5, static_global_var);
例子:文件XYBlock.m
// 全局变量
int global_var = 4;
// 静态全局变量
static int static_global_var = 5;
- (void)method
// 基本数据类型的局部变量
int var = 1;
// 对象类型的局部变量
__unsafe_unretained id unsafe_obj = nil;
__strong id strong_obj = nil;
// 静态局部变量
static int static_var = 3;
void(^Block)(void) = ^
NSLog(@"局部变量<基本数据类型> var %d", var);
NSLog(@"局部变量<__unsafe_unretained 对象类型> var %@", unsafe_obj);
NSLog(@"局部变量<__strong 对象类型> var %@", strong_obj);
NSLog(@"静态局部变量 %d", static_var);
NSLog(@"全局变量 %d", global_var);
NSLog(@"静态全局变量 %d", static_global_var);
;
var = 99;
unsafe_obj = @"99";
strong_obj = @"99";
static_var = 99;
global_var = 99;
static_global_var = 99;
NSLog(@"局部变量<基本数据类型> var %d", var);
NSLog(@"局部变量<__unsafe_unretained 对象类型> var %@", unsafe_obj);
NSLog(@"局部变量<__strong 对象类型> var %@", strong_obj);
NSLog(@"静态局部变量 %d", static_var);
NSLog(@"全局变量 %d", global_var);
NSLog(@"静态全局变量 %d", static_global_var);
NSLog(@"——————————————————————————————————————————");
Block();
控制台打印:
局部变量<基本数据类型> var 99
局部变量<__unsafe_unretained 对象类型> var 99
局部变量<__strong 对象类型> var 99
静态局部变量 99
全局变量 99
静态全局变量 99
————————————————————————————————
局部变量<基本数据类型> var 1
局部变量<__unsafe_unretained 对象类型> var (null)
局部变量<__strong 对象类型> var (null)
静态局部变量 99
全局变量 99
静态全局变量 99
四、__block修饰符
一般情况下
,对被截获变量进行赋值
操作需添加__block修饰符
。
注意:
笔试题1:
笔试题2:
1、对变量进行赋值时
需要__block修饰符:
(1)、局部变量的基本数据类型
(2)、局部变量的对象类型
不需要__block修饰符:
(1)、静态局部变量
(2)、全局变量
(3)、静态全局变量
2、__block修饰的变量变成了对象
- (void)method
__block int multiplier = 6;
int(^Block)(int) = ^int(int num)
return num*multiplier;
;
multiplier = 4;
NSLog(@"result is %d",Block(2));
打印台:
result is 8
通过编译生成XYBlock.cpp
文件内容如下:
static void _I_XYBlock_method(XYBlock * self, SEL _cmd)
__attribute__((__blocks__(byref))) __Block_byref_multiplier_0 multiplier = (void*)0,(__Block_byref_multiplier_0 *)&multiplier, 0, sizeof(__Block_byref_multiplier_0), 6;
int(*Block)(int) = ((int (*)(int))&__XYBlock__method_block_impl_0((void *)__XYBlock__method_block_func_0, &__XYBlock__method_block_desc_0_DATA, (__Block_byref_multiplier_0 *)&multiplier, 570425344));
(multiplier.__forwarding->multiplier) = 4;
NSLog((NSString *)&__NSConstantStringImpl__var_folders_0k_f0kgkdz15jj50b2svwft4fzw0000gn_T_XYBlock_4b696f_mi_0,((int (*)(__block_impl *, int))((__block_impl *)Block)->FuncPtr)((__block_impl *)Block, 2));
结构体__Block_byref_multiplier_0
:
struct __Block_byref_multiplier_0
void *__isa;
__Block_byref_multiplier_0 *__forwarding;
int __flags;
int __size;
int multiplier;
;
注意:
对于栈上
的__forwarding
指向的是_block变量本身
。
五、Block的内存管理
1、类型
block有三种类型
- NSGlobalBlock ( _NSConcreteGlobalBlock )全局类型的block
- NSStackBlock ( _NSConcreteStackBlock )栈类型的block
- NSMallocBlock ( _NSConcreteMallocBlock )堆类型的block
2、内存分配
3、Block的Copy操作
Block的类别 | 源的配置存储域 | Copy效果 |
---|---|---|
_NSConcreteGlobalBlock | 程序的数据区域 | 什么也不做 |
_NSConcreteStackBlock | 栈 | 从栈复制到堆 |
_NSConcreteMallocBlock | 堆 | 引用计数增加 |
4、栈上的Block的销毁
5、栈上Block的Copy
6、栈上__block变量的Copy
7、__forwarding总结
- 不论在任何内存位置,都可以顺利的访问同一个__block变量;
六、Block的循环引用
1、__weak消除循环引用
2、__block消除循环引用
在MRC下
,不会产生循环引用。在ARC下
,会产生循环引用,引用内存泄露。
ARC下的引用循环:
解决方案:
- 通过__block变量可控制对象的持有期间;
- 在执行Block时可动态地决定是否将
nil
或其他对象赋值在__block变量中; - 为避免循环引用
必须执行
Block;
以上是关于iOS Block的主要内容,如果未能解决你的问题,请参考以下文章