Objective-C的代码块(Block)

0x01 什么是代码块

Objective-C中的代码块(Block)是iOS4.0和Mac OS X 10.6之后的系统中引进的对C语言的扩展。

它在Xcode和Clang编译器中是有效的,但它并不属于ANSI C标准。

代码块对象(通常称为代码块)除了函数中的代码,还包含变量绑定。代码块有时也称为闭包(closure)。

代码块包含两种类型的绑定:

  • 自动型:自动绑定(automatic binding)使用的是栈内存;
  • 托管型:托管绑定(managed binding)使用的是堆内存。

关于代码块的内存绑定未来会有更详细的笔记。

 

因为代码块实际上是由C语言实现的,所以它们在各种以C作为基础的语言内都是有效的,包括Objective-C、C++以及Objective-C++。

 

0x02 代码块的定义和实现

Objective-C的代码块(Block)_第1张图片

一般可以用以下关系来表示一个代码块:

(^blockname)(list of arguments) = ^(arguments){body;};

代码块通常显得简洁明了:

  • 编译器可以通过代码块的内容推导出返回类型,所以返回类型是可以省略的;
  • 如果代码块没有参数arguments,那也可以省略。

最简化的示例如下:

void (^theBlock)() = ^{printf("Hello Block!\n");};

 

0x03 代码块的使用

使用代码块

可以像使用函数一样使用代码块。

调用代码块的时候不需要使用幂符号(^),只有声明代码块的时候才需要:

int result = square_block(5);

 

直接内联代码块

使用代码块时通常不需要创建一个代码块变量,而是在代码内联代码块的内容。 
可以在需要代码块做参数的方法或函数里面直接使用:

NSArray *array = [NSArray arrayWithObjects:
                  @"Amir", @"Mishal", @"Irrum", @"Adam", nil];
NSLog(@"Unsorted Array %@", array);

NSArray *sortedArray = [ array sortedArrayUsingComparator:
    ^(NSString *object1, NSString *object2) {
        return [object1 compare:object2];
    }//Using a Block Directly
];

NSLog(@"Sorted Array %@", sortedArray);

 

使用typedef关键字

代码块在定义的时候经常会比较长,容易出错,此时可以用typedef帮忙:

typedef double (^MKSampleMultiply2BlockRef)(double c, double d);

这行代码定义了一个名为MKSampleMultiply2BlockRef的代码块变量类型,它包含两个double型的参数,并且返回一个double型的数值。

有了typedef,就可以像下面这样使用这个代码块变量:

#include 

typedef double (^MKSampleMultiply2BlockRef)(double c, double d);

int main(int argc, const char * argv[])
{
    MKSampleMultiply2BlockRef multiply2 = ^(double c, double d) { return c * d; };
    printf("%f, %f", multiply2(4, 5), multiply2(5, 2));
    return 0;
}

 

0x04 代码块和变量

代码块被声明后会捕捉创建点的状态。代码块可以访问函数用到的标准类型变量:

  • 全局变量,包括在封闭范围内声明的本地静态变量
  • 全局函数(明显不是真实的变量)
  • 封闭范围内的参数
  • 函数级别(即与代码块声明时相同的级别)的__block变量,它们是可修改的变量
  • 封闭范围内的非静态变量会被获取为常量
  • Objective-C的实例变量
  • 代码块内部的本地变量

 

本地变量

本地变量就是与代码块在同一范围内生命的变量:

typedef double(^MKSampleMultiplyBlockRef)(void);
double a = 10,b = 20;

MKSampleMultiplyBlockRef multply = ^(void){return a*b;};
NSLog(@"%f",multiply());      //200
a = 20;
b = 50;
NSLog(@"%f",multiply());      //200

运行后可以看到该段代码的两个NSLog语句都输出200.00,为什么呢?

因为代码块会在定义时复制并保存本地变量的状态。

 

全局变量

 在上面的本地变量示例中,我们说过变量与代码块拥有相同的有效范围,所以可以根据需要把变量标记成静态的(全局的):

static double a = 10, b = 20;

MKSampleMultiplyBlockRef multiply = ^(void){ return a * b; };
NSLog(@"%f", multiply());      //200
a = 20;
b = 50;
NSLog(@"%f", multiply());      //1000

运行后,第一个NSLog语句输出200.00,第二个NSLog语句输出1000.00。

因为代码块保留的是全局变量的a和b,第二个NSLog语句执行之前,全部变量a和b的值已经被修改,所以输出也随之改变。

 

参数变量

代码块中的参数变量与函数中的参数变量具有相同的作用:

typedef double (^MKSampleMultiply2BlockRef)(double c, double d);
MKSampleMultiply2BlockRef multiply2 = ^(double c, double d) { return c * d; };
NSLog(@"%f, %f", multiply2(4, 5), multiply2(5, 2));

//Output:
//More Typedefs[1818:30515] 20.000000, 10.000000

 

__block变量

本地变量会被代码块作为常量获取到。

如果想要修改它们的值,必须把它们声明为可修改的,否则将出现编译错误:

//the following code will fail to compile:
double c = 3;
MKSampleMultiplyBlockRef multiply = ^(double a, double b) { c = a * b; };

//To fix the compilation error:
__block double c = 3;
     MKSampleMultiplyBlockRef multiply = ^(double a, double b)
     { c = a * b; };

以下变量是无法声明为__block类型的:

  • 没有长度可变的数组;
  • 没有包含可变长度数组的结构体。

 

代码块内部的本地变量

 代码块内部的本地变量与函数中的本地变量具有同样的作用:

#include 

typedef void (^MKSampleMultiplyBlockRef) (double a, double b);

int main(int argc, const char * argv[])
{
    __block double c = 3;
    MKSampleMultiplyBlockRef multiply = ^(double a, double b) { c = a * b; };
    
    multiply(4, 5);                          //a = 4, b = 5
    printf("c = %f\n", c);
    
    void (^MKSampleBlock)(void) = ^(void) {
        double a = 4;                        //参数列表为空,但在代码块内部定义了变量
        double c = 2;
        printf("a * c = %f\n", a * c);
    };
    
    MKSampleBlock();                         //执行代码块,效果与本地变量相同
    return 0;
}

//Output:
//c = 20.000000
//a * c = 8.000000
//Program ended with exit code: 0

 

你可能感兴趣的:(Objective-C,Objective-C基础教程)