前些天在项目中接触了比较多的block的应用,觉得它是一个很有意思,并且很好用的东西,所以在这里做一个记录,同时要借鉴几位大神的文章,也是为了自己能够更深入的了解一下block。
首先我们来了解一下什么是block?
- block是一个c级别的语法并具有运行时特性,比较类似c函数,但较之c函数灵活,主要体现在栈内存、堆内存的引用
(会在下面简单的说明一下堆和栈)
,我们可以用block来传值,也可以将block当做一个参数传给其他的函数或者block。
简单介绍一下什么是堆(heap)和栈(stack)?
-
栈区(stack):由系统自动分配,一般存放函数参数值、局部变量的值等。由编译器自动创建与释放。其操作方式类似于数据结构中的栈,即后进先出、先进后出的原则。
-
堆区(heap):一般由程序员申请并指明大小,最终也由程序员释放。如果程序员不释放,程序结束时可能会由OS回收。对于堆区的管理是采用链表式管理的,操作系统有一个记录空闲内存地址的链表,当接收到程序分配内存的申请时,操作系统就会遍历该链表,遍历到一个记录的内存地址大于申请内存的链表节点,并将该节点从该链表中删除,然后将该节点记录的内存地址分配给程序。
那么block长什么样子?
从别的帖子里粘过来的图

我们如何来定义block?
上段代码
#import <UIKit/UIKit.h>
//定义一个block
typedef void (^FirstBlock)(NSString *message);
@interface ViewController : UIViewController
//在.h文件中也可以这么定义
@property (copy, nonatomic) void (^SecondBlock)(NSString *error);
//或者去copy一个上面定义的block
@property (copy, nonatomic) FirstBlock message;
//我们也可以把它当做一个参数来传
- (instancetype)initWithShowMessage:(void(^)(NSString *message))messageBlock;
//也可以这样用 当然这是一个很鸡肋的写法,这是有多闲才会自己写了一个block自己来调。
void (^fourthBlock)(NSString *error) = ^(NSString *error) {
NSLog(@"%@",error);
};
fourthBlock(@"这是一个error的演示");
-
为什么block要用copy修饰?
block本身是像对象一样可以retain,和release。但是,block在创建的时候,它的内存是分配在栈(stack)上,而不是在堆(heap)上。他本身的作于域是属于创建时候的作用域,一旦在创建时候的作用域外面调用block将导致程序崩溃。
使用retain也可以,但是block的retain行为默认是用copy的行为实现的,因为block变量默认是声明为栈变量的,为了能够在block的声明域外使用,所以要把block拷贝(copy)到堆,所以说为了block属性声明和实际的操作一致,最好声明为copy。
如何使用block?
我们来写一个简单的例子
- (void)viewDidLoad {
[super viewDidLoad];
//在这里实现了block被调用后执行的操作
NSString *testStr = @"great";
self.message = ^(NSString *message) {
NSLog(@"%@%@",message,testStr);
};
[self testBlock];
}
- (void)testBlock {
//在此函数里调用了block 并赋值
if (self.message) {
self.message(@"测试测试");
}
}
打印结果
2016-07-21 11:04:18.954 BlockDemo[3674:127573] 测试测试great
作为参数时
ViewController *vc = [[ViewController alloc]initWithShowMessage:^(NSString *message) {
NSLog(@"%@",message);
}];
- 例子并不是很合适,但我们可以看出,使用起来异常方便,同样可以用来页面间传值、回调。
- 可以看出,block代码块可以访问外部变量,但是如果在代码块中修改外部变量的话,就会报错。
如果我们要修改外部变量,就需要加__block
//在这里实现了block被调用后执行的操作
__block NSString *testStr = @"great";
self.message = ^(NSString *message) {
testStr = @"good";
NSLog(@"%@%@",message,testStr);
};
2016-07-21 11:18:27.237 BlockDemo[3834:136489] 测试测试good
那么__block
与__weak
有什么区别?
-
__block
不管是ARC还是MRC模式下都可以使用,可以修饰对象,还可以修饰基本数据类型。 -
__weak
只能在ARC模式下使用,也只能修饰对象(NSString),不能修饰基本数据类型(int)。 -
__block
对象可以在block中被重新赋值,__weak
不可以。
如何实现两个页面之间的传值?
我们先来假定一个例子,两个页面A和B,A需要B给点个一个A的页面颜色。B给定颜色之后,A来改变背景颜色。
首先,页面B中,定义一个block,参数给了一个color。
#import <UIKit/UIKit.h>
@interface MineViewController : UIViewController
@property (copy, nonatomic) void(^changeColor)(UIColor *color);
@end
然后,这是在页面A的点击事件中,从B的block中获取颜色值,并更改。
- (IBAction)pushMine:(id)sender {
MineViewController *mvc = [[MineViewController alloc]init];
mvc.changeColor = ^(UIColor *color) {
self.view.backgroundColor = color;
};
[self presentViewController:mvc animated:YES completion:nil];
}
最后,这是在B页面中的点击事件
- (IBAction)backClick:(id)sender {
if (self.changeColor) {
self.changeColor([UIColor greenColor]);
}
[self dismissViewControllerAnimated:YES completion:nil];
}
是不是很简单,和代理有一点像,下面我们来说说,代理和block。
delegte和block有什么区别?
- delegate较之block成本更低,block出栈需要将使用的数据从栈内存拷贝到堆内存,当然对象的话就是加计数,使用完或者block置nil后才消除;delegate只是保存了一个对象指针,直接回调,没有额外消耗。
- block写法更简单,不需要写protocol、函数等等。
- delegate是‘一对一’的,对同一个协议,一个对象只能设置一个代理delegate。
- delegate可以声明多个函数,比如数据请求时,可以用多个函数返回:请求是否已经开始、是否收到了数据、数据是否已经接受完成、数据接收失败。block做起这些来恐怕并不容易。
网友评论