Block

前些天在项目中接触了比较多的block的应用,觉得它是一个很有意思,并且很好用的东西,所以在这里做一个记录,同时要借鉴几位大神的文章,也是为了自己能够更深入的了解一下block。

首先我们来了解一下什么是block?

  • block是一个c级别的语法并具有运行时特性,比较类似c函数,但较之c函数灵活,主要体现在栈内存、堆内存的引用(会在下面简单的说明一下堆和栈),我们可以用block来传值,也可以将block当做一个参数传给其他的函数或者block。

简单介绍一下什么是堆(heap)和栈(stack)?

  • 栈区(stack):由系统自动分配,一般存放函数参数值、局部变量的值等。由编译器自动创建与释放。其操作方式类似于数据结构中的栈,即后进先出、先进后出的原则。

  • 堆区(heap):一般由程序员申请并指明大小,最终也由程序员释放。如果程序员不释放,程序结束时可能会由OS回收。对于堆区的管理是采用链表式管理的,操作系统有一个记录空闲内存地址的链表,当接收到程序分配内存的申请时,操作系统就会遍历该链表,遍历到一个记录的内存地址大于申请内存的链表节点,并将该节点从该链表中删除,然后将该节点记录的内存地址分配给程序。

那么block长什么样子?

从别的帖子里粘过来的图


Block_第1张图片

我们如何来定义block?

上段代码


#import 

//定义一个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有什么区别?
  1. __block不管是ARC还是MRC模式下都可以使用,可以修饰对象,还可以修饰基本数据类型。
  2. __weak只能在ARC模式下使用,也只能修饰对象(NSString),不能修饰基本数据类型(int)。
  3. __block对象可以在block中被重新赋值,__weak不可以。

如何实现两个页面之间的传值?

我们先来假定一个例子,两个页面A和B,A需要B给点个一个A的页面颜色。B给定颜色之后,A来改变背景颜色。

首先,页面B中,定义一个block,参数给了一个color。

#import 

@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有什么区别?

  1. delegate较之block成本更低,block出栈需要将使用的数据从栈内存拷贝到堆内存,当然对象的话就是加计数,使用完或者block置nil后才消除;delegate只是保存了一个对象指针,直接回调,没有额外消耗。
  2. block写法更简单,不需要写protocol、函数等等。
  3. delegate是‘一对一’的,对同一个协议,一个对象只能设置一个代理delegate。
  4. delegate可以声明多个函数,比如数据请求时,可以用多个函数返回:请求是否已经开始、是否收到了数据、数据是否已经接受完成、数据接收失败。block做起这些来恐怕并不容易。

个人理解,欢迎补充,欢迎指正。

你可能感兴趣的:(Block)