文章目录
  1. 1. Block由来
  2. 2. Block语法
  3. 3. Block类型变量
  4. 4. Block截获(Capture)自动变量

前面有一篇文章讲到iOS开发之block终极篇,今天给大家分享iOS开发之Block基础篇。

这期总结的内容为Block,Block是C语言的扩充功能,也被称为带有自动变量(局部变量)的匿名函数。Block在网络请求的回调中起着举足轻重的作用。下面就来一步步的探索它的由来和使用方法。

Block由来


先来看看C语言中标准的函数,如下:

int func(int count);

它声明了名称为func的函数。下面看看怎么调用的

int result = func(10);

如果你知道函数指针,那么应该熟悉下面这个式子,它不用知道函数名也能够使用函数。

int result = (*funcptr)(10);

但是,实际上仍然需要知道函数的名称。在赋值函数指针时还是需要知道函数名称的,如下

int *funcptr (int)= &func;

为什么需要函数指针呢?

函数指针是为了获取函数的地址方便将函数作为参数时的调用.

为什么会出现匿名函数?

是为了代替函数指针,简化函数的书写。

​Block可以真正的实现不需要函数名称的函数,方便了函数的调用。通俗的讲Block就是一段匿名代码块。

下面通过普通的函数与匿名函数Block作对比来展现匿名函数的魅力。

@interface ButtonCallbackObject : NSObject
{
    int buttonId_;
}
@implementation ButtonCallbackObject
- (void)initWithButtonId:(int)buttonId
{
    self = [super init];
    buttonId_ = buttonId
    return self;
}

- (void)callback:(int)event
{
    NSLog(@“buttonId = %d event = %d\n”,buttonId_, event);
}
@end

使用该类,由于对象保持按钮的ID,因此保持对象即可。回调函数的使用如下:

void setButtonCallbacks()
{
    for (int i = 0; i < BUTTON_MAX; i++) {
        ButtonCallbackObject *callbackObj = [[ButtonCallbackObject alloc] initWithButtonId:i];
        setButtonCallbackUsingObject(BUTTON_IDOFFSET,callbackObj);
    }
}

setButtonCallbackUsingObject函数实现的功能是将callbackObj作为参数,在内部通过callbackObj对象实现对callback函数的调用。这么做的有两个不友好的地方,一是每次回调callback函数时都需要创建一个ButtonCallbackObject对象,二是还要在类中定义callback方法。来看看Block的实现

void setButtonCallback()
{
    for (int i = 0; i < BUTTON_MAX; ++i) {
        setButtonCallbackUsingBlock(BUTTON_IDOFFSET + i, ^(int event) {
            printf("buttonId = %d event = %d", i, event);
        });
    }
}

通过使用Block使得方法的回调变得更加的简洁。

Block语法


^(int event) {
        printf("buttonId = %d event = %d", i, event);
}

实际上,该Block语法是使用了省略的方式,其完整的形式如下:

^void (int event) {
        printf("buttonId = %d event = %d", i, event);
}

完整的Block语法是如下形式
^ 返回值 (参数类型) {表达式}
“返回类型”同C语言的函数的返回值类型,”参数列表”同C语言函数的参数列表,”表达式”同C语言函数中允许使用的表达式。但是函数中return的返回值类型,必须同申明的类型一致。

返回值类型可以省略。省略返回值时,如果表达式中有return语句就使用该返回值的类型,如果表达式中没有return语句就是void类型。

当Block没有参数时参数类型也可以省略。最简形式如下:
^ {表达式}

Block类型变量

我们已经知道Block语法单从其外型上看,除了没有名称以及带了” ^ “外,同C语言的函数定义相同。在定义C语言函数时,就可以将所定义的地址赋值给函数指针变量中。

int func(int count)
{
    return count + 1;
}
int (*funcptr)(int) = &func;

这样一来,函数func的地址就能赋值给函数指针类型变量funcptr中了。

同样地,在Block语法下,也可以将Block赋值给声明为Block类型的变量中。声明Block类型变量的示例如下:

int (^blk)(int)

与前面的C函数指针源代码对比,声明Block类型变量仅仅是将声明函数指针类型变量的” * ”变为”
^ ”。该Block类型变量与一般的C语言变量完全相同。该Block的意思是。定义的Block类型的变量名称是blk,该block返回值类型为int,参数类型为int。下面我们就可以把符合相同类型的block赋值给这个变量。

int (^blk)(int) = ^int (int count) { return count + 1 };

block变量的定义与block函数定义可以从外观上找到很多相似点。变量的定义将block函数的返回类型放到了前面,并且在原来声明返回值类型的地方替换成了block变量名并用括号包起来。来看看几个block变量如普通变量一般的使用。

int (^blk1)(int) ;
blk1 = blk;

该例子有定义了一个blk1变量,参数和返回值都与blk相同。所以可以直接将blk赋值给blk1变量。

block变量还可以作为函数的参数来传递。

- (int)methodUsingBlock:(blk_t)blk rate:(int)rate
{
    return blk(rate);
}

Block类型变量可完全像通常的C语言变量一样。这里可以顺便理解一下回调的意思,这里的回调即是将block的作为参数传递给其他函数,当该函数执行到这个block时,需要调用它的实现部分。即代表回调了这个block。

Block截获(Capture)自动变量


通过Block语法和Block类型变量的说明,我们已经理解了“带有自动变量的匿名函数”中的匿名函数。而“带有自动变量”究竟是什么意思呢?“带有自动变量值”在Blocks中的表现为“截获自动变量的值”。截获自动变量值的实例如下:

int main()
{
    int val = 10;
    const char *fmt = “val = %d\n”;
    void (^blk)(void) = ^{ printf( fmt, val);};
    val = 2;
    ftm = "This value were changed. val = %d\n"
    //    
    blk();
    //
    return 0;
}

该源代码中,Block语法的表达式使用的是它之前声明的自动变量fmt和val。Blocks中,Block表达式截获所使用的自动变量的值,即保存该自动变量的瞬间值。因为Block表达式保存了自动变量的值,所以在执行Block语法后,即使改变Block中使用的自动变量的值也不会影响Block执行的自动变量的值。该源代码就在Block语法后改写了Block中的自动变量val和fmt。
下面来看看执行结果:

val = 10

执行结果并不是改变后的值“These values were changed. val = 2”,而是Block语法的瞬间值。该Block的语法在执行时,字符串指针“val = %d\n”被赋值到自动变量fmt中,int值10被赋值到自动变量val中,因此这些值被保存(即截获),从而在block执行时使用。
这就是自动变量的截获。

那如果要在block中修改截获的变量怎么办?实现如下

int __block val = 0;
void (^blk)(void) = ^{val = 1;};
blk();
NSLog(@"val = %d",val);

来看看执行结果:

val = 1

使用block说明符的自动变量可在Block中赋值,该变量称为block变量。

最后来来看看Objective-C变量的截获

NSMutableArray *array = [[NSMutableArray alloc] init];
void (^blk)(void) = ^{
    id obj = [[NSObject alloc] init];
    [array addObject:obj];
    NSLog(@"array count = %d",array.count);
};
blk();

执行结果为

array count = 1

可以看出可变数组对象被block截获之后可以对其进行操作。

本文由程序员头条管理员蒋小飞原创文章,转载务必注明出处。

蒋小飞微信公众号iOSTalk:JoneTalk

我叫小飞,iOS开发者,现就职于智能家居领域,坐标浙江杭州.iOSTalk用于记录iOS开发技巧,以及发表个人成长之路的一些观点.

iostalk


本文出处程序员头条:http://www.iswifting.com/2015/08/10/ios-block-basic-article/
转载请在开头注明本文出处。

欢迎关注本站微信公众号:为程序员提供最优质的博文、最精彩的讨论、最实用的开发资源;提供最新最全的编程学习资料:PHP、Objective-C、Java、Swift、C/C++函数库、.NET Framework类库、J2SE API等等.并不定期奉送各种福利.
微信公众号猿圈:CodePush

文章目录
  1. 1. Block由来
  2. 2. Block语法
  3. 3. Block类型变量
  4. 4. Block截获(Capture)自动变量