Block内部定义
我们想要知道Block的实现过程,首先我们深入Block,下图是对Block的结构是在栈中的结构,图片来自互联网。
下面代码为对应结构体定义:
struct Block_descriptor {
unsigned long int reserved;
unsigned long int size;
void (*copy)(void *dst, void *src);
void (*dispose)(void *);
};
struct Block_layout {
void *isa;
int flags;
int reserved;
void (*invoke)(void *, ...);
struct Block_descriptor *descriptor;
/* Imported variables. */
};
Block_layout就是对block结构体的定义:
isa指针:指向表明该block类型的类。
flags:按bit位表示一些block的附加信息,比如判断block类型、判断block引用计数、判断block是否需要执行辅助函数等。
reserved:保留变量,我的理解是表示block内部的变量数。
invoke:函数指针,指向具体的block实现的函数调用地址。
descriptor:block的附加描述信息,比如保留变量数、block的大小、进行copy或dispose的辅助函数指针。
variables:因为block有闭包性,所以可以访问block外部的局部变量。这些variables就是复制到结构体中的外部局部变量或变量的地址。
Block类型
从上面Block的定义Block_layout结构体中isa指针类型分为三种:
- _NSConcreteGlobalBlock:全局的静态block,不访问任何外部变量,不会涉及到任何拷贝,比如一个空的block。例如:
^{print("hello world");}
- _NSConcreteStackBlock:保存在栈中的block,引用周围变量,当函数返回时被销毁。
char a = 'A';
^{ print("%c\n",a); }
- _NSConcreteMallocBlock:保存在堆中的block。该类型是由栈中Block ( Block_copy / Copy ) 复制到堆中形成的。
//只需要加上copy就可以
char a = 'A';
[^{ print("%c\n",a); } copy];
需要注意的是我们一般创建的Block默认都是在栈中的,而系统的基础数据类型也是在栈中,那么又能使用同一个栈,倒是元素丢失,出现野指针,我们在开发中使用安全可以在定义Block时使用copy属性迁移到栈中。
Block使用
- 使用typedef来定义Block提高程序可读性。
//适应typedef提高可读性
typedef void (^ReturnTextBlock)(NSString *showText);
@interface nickNameViewController : UIViewController
@property (nonatomic, copy) ReturnTextBlock returnTextBlock;
- (void)returnText:(ReturnTextBlock)block;
@end
- 使用安全我们定义Block使用copy属性
@property (nonatomic, copy) ReturnTextBlock returnTextBlock;
- 外部变量使用__block关键字,可在Block中修改,默认不能修改
__block int age = 0;
void (^addAge) = ^{
age += 1;
}
NSLog(%d,age )
- 避免循环引用
__block int age = 0;
__weak id weakSelf = self;
void (^push) = ^{
[weakSelf push];
}
PS
在实际开发Block的使用非常多,作为方法参数,比如网络请求的失败和成功回调,SDWebImage也同样使用多种闭包来完成对应的功能,我记得曾经用的一个插件把Selecte做成Block也是很方便,但是在使用的时候要注意。今天在博客中介绍堆和栈并非传统意义的堆栈,是程序在运行时在内存的分布情况,之后我们将详细介绍。