Block 学习笔记

一 、 什么是block?

A block is an anonymous inline collectionof code, and sometimes also called a “closure”.  

--Apple

闭包是一种函数,它能够读取其它函数的内部变量。

block 也相当于内联函数,执行速度快,简单方便;

二、 为什么要使用block ?

  1.使用简单、保持代码在一起、苹果推荐。。。。

  2. Block除了能够定义参数列表、返回类型外,还能够获取被定义时的词法范围内的状态(比如局部变量),并且在一定条件下(比如使用__block变量)能够修改这些状态。此外,这些可修改的状态在相同词法范围内的多个block之间是共享的,即便出了该词法范围(比如栈展开,出了作用域),仍可以继续共享或者修改这些状态。

3. 一般情况,block只能访问变量,而不能修改变量的值。而要想修改变量的值,有两种方式,在定义变量的时候:

1).static关键字;

2).__block去修饰


三、 block 的使用

 block 的简单用法:

#import "BlockTestViewController.h"
#import <stdio.h>
#import <mach/mach_time.h>

@interface BlockTestViewController ()

@property (nonatomic, copy) void (^myBlock)();//使用block 作为属性

@end

//block 定义

//无返回值 无参的block
void (^block)() = ^{
    NSLog(@"block");
};

//无返回值  int 类型参数
void (^block1)(int) = ^(int n){
    NSLog(@"block1 === %d",n);
};


//无返回值  block作为参数
void (^block2)(void(^block)()) = ^(void(^block)()){
    NSLog(@"block2");
    block();
};


//返回float  block 作为参数   --  计算block 方法执行所消耗的时间
float timeBlock(void(^block)()){
    uint64_t startTime = mach_absolute_time();//获取CPU tickCount 的计数值
    block();
    uint64_t endTime = mach_absolute_time();
    uint64_t elapsed = endTime - startTime;
    return (float)elapsed / NSEC_PER_SEC;
}

@implementation BlockTestViewController
@synthesize myBlock;

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if (self) {
        // Custom initialization
    }
    return self;
}

- (void)viewDidLoad
{
    [super viewDidLoad];
	// Do any additional setup after loading the view.
    self.title = @"Block" ;
    NSLog(@"time : %f",timeBlock(^{
        int n = 10000;
        for (int i = 0; i < n; i ++) {
        }
    }));
    self.myBlock = ^{
        NSLog(@"myBlock");
    };
    
    self.myBlock();
    block();
    
    block1(10);
    
    block2(self.myBlock);
    
    [self executeBlock:^{
        NSLog(@"111111");
    }completion:^(BOOL finished){
//        if (finished) {//可以根据finished 属性来处理相应操作
//            return ;
//        }
        NSLog(@"222222");
    }];
    
}

//含有多个block 作为参数 的方法 
/*
  ^表示是block 类型,
 () 表示无参  
 void 表示返回值为空的block
 */
- (void)executeBlock:(void(^)())block  completion:(void (^)(BOOL finished))completion{
    NSLog(@"begin");
    block();
    NSLog(@"middle");
    completion(YES);
}


- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

@end

如何在 block 中修改外部变量:

      考虑到 block 的目的是为了支持并行编程,对于普通的 local 变量,我们就不能在 block 里面随意修改(原因很简单,block 可以被多个线程并行运行,会有问题的),而且如果你在 block 中修改普通的 local 变量,编译器也会报错。那么该如何修改外部变量呢?有两种办法,第一种是可以修改 static 全局变量;第二种是可以修改用新关键字 __block 修饰的变量

 __block int i = 0;
    static int j = 0;
    void (^block4)() = ^(){
        i = i + 1;
        j = j+ 2;
    };
    block4();
    NSLog(@"i = %d, j = %d", i , j);//i = 1; j = 2



block 数组

void (^blocks[2])(void) = {
    ^(void){
        NSLog(@"block[0]");
    },      //注意逗号
    ^(void){
        NSLog(@"block[1]");
    }
};
调用方式:
    blocks[0]();
    blocks[1]();
//    blocks[2]();//当然也会有数组越界
当然也会存在数组越界的行为,如果在blocks[2]中我们即使生命了两个但只实现了一个block的实现,当调用block[1]的时候同样也会发生数组越界的行为;




四、 使用规则

  block在使用过程中要避免引用环的出现, 如下例子,在block 中使用self 此时,block会retain  self 对象,使self的引用计数加1,从而会永远无法执行dealloc 方法,因为在self 与block 之间产生了引用环;

ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url] ;
[request setCompletionBlock:^{
    [self imageDidFinishLoading:request];  
}];
解决方法:若引用self , 此时self 在block 中不会被计数 +1 

ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url] ;
__block ClASS *weakSelf = self;
[request setCompletionBlock:^{
    [self imageDidFinishLoading:request];  
}];
 

谨记!

block 是分配在 stack 上的,这意味着我们必须小心里处理 block 的生命周期。比如如下的做法是不对的,因为 stack 分配的 block 在 if 或 else 内是有效的,但是到大括号 } 退出时就可能无效了:

    void (^block3)(void) = 0;
    
    if (true) {
        block3 = ^{ printf("true\n"); };
    } else {
        block3 = ^{ printf("false\n"); };
    }
    block3();


在block函数体中使用的实例变量将自动retained ,体会:目的是防止return时候没有返回真正的带有空间地址的变量;

eg:

如果我们在Objective-C的方法中使用block时,以下几个和记忆体管理的事是需要额外注意的:

l 若直接存取实体变数(instance variable),self的参考计数将被加1。

l 若透过变数存取实体变数的值,则只变数的参考计数将被加1。

以下程式码说明上面两种情况,在这个假设instanceVariable是实体变数:

 1: dispatch_async (queue, ^{

 2: // 因为直接存取实体变数instanceVariable ,所以self 的retain count 会加1

 3: doSomethingWithObject (instanceVariable);

 4: });

 5: id localVaribale = instanceVariable;

 6: dispatch_async (queue, ^{

 7: //localVariable 是存取值,所以这时只有localVariable 的retain count 加1

 8: //self 的 return count  并不会增加。

 9: doSomethingWithObject (localVaribale);

 10: });



未完待续。。。




 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值