- block本质上也是一个
OC对象
,它内部也有个isa
指针 - block是封装了函数调用以及函数调用环境的OC对象
- block是封装
函数
及其上下文
的OC对象
block 语法:
^ 返回值类型 参数列表 表达式
^ (NSString *) (NSString *bookId){}
省略返回值写法:
^(void){};
省略返回值及参数写法:
^{};
Block定义声明使用
//声明
typedef void (^Block)();
typedef int (^MyBlock)(int , int);
typedef void(^ConfirmBlock)(BOOL isOK);
typedef void(^AlertBlock)(NSInteger alertTag);
//定义属性
@property (nonatomic,copy) MyBlock myBlockOne;
//使用
self.myBlockOne = ^int (int ,int){
//TODO
}

Block的类型
block 有三种类型:
-
全局块(_NSConcreteGlobalBlock)
-
堆块(_NSConcreteMallocBlock)
-
栈块(_NSConcreteStackBlock)
存储区域如下:
存储区域
-
全局块存在于全局内存中,相当于单例;
-
栈块存在于栈内存中,超出其作用域则马上被销毁;
-
堆块存在于堆内存中,是一个带引用计数的对象,需要自行管理内存
A. 全局Block(_NSConcreteGlobalBlock)
- 数据区域(.data区)
- 在block内部不会访问任何外部变量,或者只使用静态变量和全局变量。
//不会访问任何外部变量
- (void)GlobalBlock{
void (^blk)(void) = ^ {
};
blk();
NSLog(@"blk>>>>:%@",blk);
}

//只使用静态变量和全局变量
/** 全局变量 */
int global_count = 10;
/** 静态全局变量 */
static int static_global_count = 10;
int main(int argc, const char * argv[]) {
/** 静态局部变量 */
static int static_count = 10;
void (^block)(void) = ^ {
global_count = 11;
static_global_count = 11;
static_count = 11;
};
block();
return 0;
}

B. 堆Block(_NSConcreteMallocBlock)
- 位于堆区
- 在Block内部使用局部变量或者OC的属性,并且赋值给强引用或者Copy修饰的变量
- (void)MallocBlock{
int val = 10;
void (^blk2)(void) = ^ {
NSLog(@">>>>:%@",@(val));
};
blk2();
NSLog(@">>>>:%@",blk2);
}

C.栈Block(_NSConcreteStackBlock)
- 位于栈区,栈内有效,出栈后销毁。
- 与MallocBlock一样,可以在内部使用局部变量或者OC的属性。但是不能赋值给强引用或者Copy修饰的变量
- 在ARC环境下,捕获局部变量、成员变量,且没有被强引用的block都是_NSConcreteStackBlock
捕获局部变量
- (void)StackBlock{
/** 局部变量 */
int count = 10;
/**
执行
或者使用void (^__weak block)(void)来指向block
*/
^{
NSLog(@"%d", count);
}();
// 打印Block对象
NSLog(@"blk25>>>>:%@", ^{
NSLog(@"%d", count);
});
}

捕获成员变量
@interface ViewController ()
@property (nonatomic, assign) int count;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(@"Block对象:%@", ^{
NSLog(@"%d", self.count);
});
^{
NSLog(@"%d", self.count);
}();
}
@end
但是在ARC下,很少存在栈上的block。因为很多情况下ARC会主动帮你把block copy一次。
举个例子:
__block int static_k = 3;
void (^myBlock)(void) = ^{
static_k++;
};
myBlock();
NSLog(@"%@",myBlock);
在MRC下,输出结果为__NSStackBlock栈。
在ARC下,输出结果为__NSMallocBlock堆。
因为在block在捕获外部变量时ARC会自动帮我们把栈上的block copy到堆上,其中还包括下面的情况系统也会主动copy一次。
以下情况会被保存在堆上:
1.手动调用copy
2.Block是函数的返回值
3.Block被强引用,Block被赋值给__strong或者id类型
4.调用系统API入参中含有usingBlcok的方法
但并不是说在ARC上就没有栈上的block。其中当Block为函数参数的时候,就需要我们手动的copy一份到堆上了。其中GCD等系统方法中本身带usingBlock的方法,不需要处理。
三种block的拷贝效果
不多说,直接上表格:
Block的类 | 副本源的配置存储域 | 赋值效果 |
---|---|---|
_NSConcreteGlobalBlock | 数据区 | 什么也不做 |
_NSConcreteStackBlock | 栈 | 从栈拷贝到堆 |
_NSConcreteMallocBlock | 堆 | 引用计数增加 |
数据区中的_NSConcreteGlobalBlock执行拷贝操作什么也不做的原因是不需要,因为_NSConcreteGlobalBlock在进程结束后才销毁,已经是广域变量了。
Block是苹果官方特别推荐使用的数据类型, 应用场景比较广泛
- 动画
- 多线程
- 集合遍历
- 网络请求回调
网友评论