iOS Block的基本使用

例行科普

Block 是 iOS4 之后添加的一种语法结构,也成为闭包,或者匿名函数,在iOS开发过程中应用较多,使用也比较方便,常见于传值、回调等。

1.几种类型的Block

Block的定义方式:

返回类型(^block变量名)(参数列表) = ^(形参列表){};

系统提供了一个定义block的宏,输入inlineBlock就会出现

<#returnType#>(^<#blockName#>)(<#parameterTypes#>) = ^(<#parameters#>) {
        <#statements#>
};
1.1 无参数无返回值的block
void(^blockName)();//声明
blockName = ^(){
    NSLog(@"调用");
};//实现
blockName();//调用

//当然也可以连一起写,看个人喜好
//void(^blockName)() = ^(){
//    NSLog(@"调用");
//};
1.2 无参数有返回值的block
int(^getNum)() = ^(){
    return 1;
};
NSLog(@"%d", getNum());
1.3 有参数无返回值的block
void(^blockName)(int) = ^(int a){
    NSLog(@"%d", a);
};
blockName(3);
1.4 有参数有返回值的block
int(^addNum)(int, int) = ^(int a, int b){
    return a + b;
};
NSLog(@"%d", addNum(242, 424));
1.5 Block的重命名

block的写法还是比较麻烦的,所以系统提供了一个重命名的功能

typedef void(^blockType)(int, int);

这样写的话,一个有两个参数,无返回值的block类型就变成了blockType,然后只需要用这个类型声明block就可以了。

@property (strong, nonatomic) blockType aBlock;

2 上面几种只是block的初级用法,下面来点中级的。

2.1 Block做为参数

Block作为参数大家应该不陌生,因为最常用的三方库AFN就使用了这种方式。这里就不贴AFN的源码了,直接仿写一个例子,当然,具体内容并没有实现,只是写了一个思路。

- (void)requestWith:(NSString *)url
               with:(void(^)(id dataSource))success
               with:(void(^)(NSError *error))failure
{
    if (success) {
        success(@"你获取的数据");
    }
    if (failure) {
        failure([NSError new]);// 报错信息
    }
}

//调用
[self requestWith:@"url" with:^(id dataSource) {
    //请求成功
} with:^(NSError *error) {
    //请求失败
}];
2.2 Block作为返回值

Block作为返回值看起来就比较奇怪了,最常见的就是masomry了,对就是那个点来点去的,看起来高大上的语法,也就是链式编程。
先在ViewController里声明了一个属性,来存放数据

@property (nonatomic, assign)int result;

然后写返回block的方法:

- (ViewController *(^)(int))addNum
{
    return ^(int number){
        _result += number;
        return self;
    };
}

调用:

self.addNum(1).addNum(2).addNum(3);
NSLog(@"%d", _result);

解释起来应该就是这样的:
self.addNum相当于get方法的调用:[self addNum];
self.addNum返回的是一个block,所以你可以给他一个参数1,于是写成这样:self.addNum(1)
然后block返回的又是self,所以继续调用addNum
如此循环下去.......
Viewcontroller可以换成任何一个类,实现方式是一样的

2.3 Block对外部变量的修改
int a = 3;
void(^block)() = ^{
  NSLog(@"%d",a);
};
a = 5;
block();

你可以看到,打印出来的a的值是5,也就是说,block获取的是a的值,也就是3,所以当a变成5的时候,block内部打印的值并没有改变。

static int a = 3;
void(^block)() = ^{
    NSLog(@"%d",a);
};
a = 5;
block();

这个应该就不用解释了,和上面是相反的结果,block内部打印的值会随着a的值改变而改变,也就是说block获取的是a的指针。
总结起来就是:
(1)如果是局部变量,Block获取的是值。
(2)如果是静态变量,全局变量, __block修饰的变量,block获取的是指针。
说到这就得说一下这个 __block修饰符了,正常情况下block内部是不允许修改局部变量的值的,但是可以修改静态变量和全局变量,为什么?上面提到了,一种是获取值,一种是获取指针,获取值的当然就不让改了,所以需要在局部变量前加个修饰符 __block:

__block int b = 7;
void(^block)() = ^{
    b = 20;
};

这样block获取的就是指针了,也就可以修改了。

3 Block传值

估计这是提到block第一个就想到的功能了,block可以实现页面间的传值功能,直接上例子。
先在第二个页面的.h文件里声明一个block

typedef void(^block)(NSString);

@interface ViewControllerTwo : UIViewController

@property (strong, nonatomic) block aBlock;

@end

然后写下回调方法

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    if (self.aBlock) {
        self.aBlock(@"回调传值");
    }
    [self dismissViewControllerAnimated:YES completion:nil];
}

然后回到第一个页面引入头文件,写跳转方法

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
    ViewControllerTwo *VC = [ViewControllerTwo new];
    VC.aBlock = ^(NSString *str){
        NSLog(@"%@", str);
    };
    [self presentViewController: VC animated:YES completion:nil];
}

这样block传值就完成了。

4 Block的循环引用

在block内部使用self之前,写上这样一句代码

__weak typeof(self) weakSelf = self;

然后将block使用的self全部替换成weakSelf就可以避免循环引用了。

结语:限于水平,本文只写了一些基本用法和注意事项,如果文中存在错误请指出,我会及时修改。

你可能感兴趣的:(iOS Block的基本使用)