9.块

原文:http://rypress.com/tutorials/objective-c/blocks

Blocks 块

块是OC的匿名函数。块特性使得能够在不同类之前传递某段代码(函数),这比去调用某个地方定义的某个方法更加直观,另外由于块是封闭的,所以可以排除外部干扰。

创建块

块是基于函数的。你可以申明一个块就像声明一个函数一样,实现一个块和实现一个函数基本上没有区别。

// main.m
#import <Foundation/Foundation.h>

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // Declare the block variable
        double (^distanceFromRateAndTime)(double rate, double time);

        // Create and assign the block
        distanceFromRateAndTime = ^double(double rate, double time) {
            return rate * time;
        };
        // Call the block
        double dx = distanceFromRateAndTime(35, 1.5);

        NSLog(@"A car driving 35 mph will travel "
              @"%.2f miles in 1.5 hours.", dx);
    }
    return 0;
}

(^)符号标示出distanceFromRateAndTime为一个块变量。就像声明函数一样,你需要指明块的返回值和参数类型等。(^)符号符号可以很形象的和指针符号做类比(比如:int *aPointer),声明块变量后向指针变量一样去使用。

块定义自身本质上就是函数的定义-只是没有函数名而已。^double(double rate, double time)标示当前块返回类型是double类型,有两个double类型的参数,然后在花括号中定义自己的处理逻辑就像普通的函数那样。

将定义赋给变量后,我们使用这个快变量如同使用普通函数那样。

无参数块

如果一个块没有任何参数,你可以在块声明中省略参数部分内容。另外块的返回值在定义的时候是可选的(可写可不写),具体如下:

double (^randomPercent)(void) = ^ {
    return (double)arc4random() / 4294967295;
};
NSLog(@"Gas tank is %.1f%% full",
      randomPercent() * 100);

当目前为止似乎块就是使用另外一种难懂的方式去定义方法。事实上它开启了全新的变成方式的大门。

封闭

在块中,访问数据和在方法中没有区别比如:本地变量,参数和全局变量全局函数。但是块是封闭的,所以他能访问非本地变量。非本变量是指定义在块外部的变量,例如下面的getFullCarName可以读取make变量的值。

NSString *make = @"Honda";
NSString *(^getFullCarName)(NSString *) = ^(NSString *model) {
    return [make stringByAppendingFormat:@" %@", model];
};
NSLog(@"%@", getFullCarName(@"Accord"));    // Honda Accord

非本地变量在块内部使用的时候其实是原变量的一个拷贝而已,换句话说你在块中是无法修改原变量的值的他们是只读的。如果强行在块中修改非本地变量的值将会出现编译错误。

Accessing non-local variables as const copies

事实上在块中事实访问到了非本地变量的一个快照,无论外部非本地变量如何被修改,在块内部这个快照的值依然保持块定义的时候非本地变量的值,不会随着非本地变量的变化而变化,如下:

NSString *make = @"Honda";
NSString *(^getFullCarName)(NSString *) = ^(NSString *model) {
    return [make stringByAppendingFormat:@" %@", model];
};
NSLog(@"%@", getFullCarName(@"Accord"));    // Honda Accord

// Try changing the non-local variable (it won‘t change the block)
make = @"Porsche";
NSLog(@"%@", getFullCarName(@"911 Turbo")); // Honda 911 Turbo

封闭性带来了和周边交换数据的良好通道,不用非要使用参数去传值,块使用本地变量如同这个变量被定义在块内部一样方便。

可变非本地变量

非本地变量禁止在块中修改应该说是一个比较安全的做法;然而有些场合你需要具有改变非本地变量值的能力,事实上是有方法的,只要在变量声明的时候加上__block:

__block NSString *make = @"Honda";

这样声明后就告诉块这个变量被直接链接了,可以修改它的值。

Accessing non-local variables by reference

就像static local variables静态本地变量在函数中的功能一样,__block化的变量也具有相似功能了。例如下面的例子创建一个块在每次调用i都是上次调用后的计算值。

__block int i = 0;
int (^count)(void) = ^ {
    i += 1;
    return i;
};
NSLog(@"%d", count());    // 1
NSLog(@"%d", count());    // 2
NSLog(@"%d", count());    // 3

块作为参数

块变量或许有用,但是在现实中块经常被用于作为参数传递。他们好比函数指针但是他们可以一种内联的方式直接定义这样更加易读易懂。

例如,下面的Car接口声明了一个行驶距离的方法。它的一个参数采用块的方式。

// Car.h
#import <Foundation/Foundation.h>

@interface Car : NSObject

@property double odometer;

- (void)driveForDuration:(double)duration
       withVariableSpeed:(double (^)(double time))speedFunction
                   steps:(int)numSteps;

@end

块的数据类型为 double (^)(double time),指明传入的块必须返回类型为double并且具有有一个double的参数。注意块参数类型的写法和声明一个参数很像只是没有块变量名而已。

实现这个方法的时候可以直接使用块参数名speedFunction来调用块。下面的例子:

// Car.m
#import "Car.h"

@implementation Car

@synthesize odometer = _odometer;

- (void)driveForDuration:(double)duration
       withVariableSpeed:(double (^)(double time))speedFunction
                   steps:(int)numSteps {
    double dt = duration / numSteps;
    for (int i=1; i<=numSteps; i++) {
        _odometer += speedFunction(i*dt) * dt;
    }
}

@end

就像你在如下main方法中看到的,块可以在方法被调用的时候直接定义:

// main.m
#import <Foundation/Foundation.h>
#import "Car.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Car *theCar = [[Car alloc] init];

        // Drive for awhile with constant speed of 5.0 m/s
        [theCar driveForDuration:10.0
               withVariableSpeed:^(double time) {
                           return 5.0;
                       } steps:100];
        NSLog(@"The car has now driven %.2f meters", theCar.odometer);

        // Start accelerating at a rate of 1.0 m/s^2
        [theCar driveForDuration:10.0
               withVariableSpeed:^(double time) {
                           return time + 5.0;
                       } steps:100];
        NSLog(@"The car has now driven %.2f meters", theCar.odometer);
    }
    return 0;
}

上述举了一个简单地块的例子,但是在标准的框架中确存在很多块的东西。 NSArray可以让你通过sortedArrayUsingComparator:使用块来对元素进行排序。UIViewanimateWithDuration:animations:方法使用块定义最终的animation的状态。

定义块类型

语法上块的类型比较杂乱,我们可以使用typedef来为块类型做签名,例如,下面的代码中新建了一个新的块类型叫做SpeedFunction,这样我们可以使用SpeedFunction来声明对应的块变量了。

// Car.h
#import <Foundation/Foundation.h>

// Define a new type for the block
typedef double (^SpeedFunction)(double);

@interface Car : NSObject

@property double odometer;

- (void)driveForDuration:(double)duration
       withVariableSpeed:(SpeedFunction)speedFunction
                   steps:(int)numSteps;

@end

总结

块提供了很多的C的函数的功能,但是相比较与函数块更加直观和灵活。

下一个章节中我们将进入IOS和OS X的错误处理环节。我们将介绍两个重要的错误类:NSException和NSError;

时间: 2024-10-11 22:43:24

9.块的相关文章

微信小程序布局之行内元素和块级元素

元素按照显示方式主要可以分为块级元素和行内元素,元素的显示方式由display属性控制. 块级元素特点总结: 1.总是在新行上开始 2.宽度的默认为width+margin-left+margin-right+padding-left+padding-right刚好等于父级元素的内容区域宽度,即父元素的width.当设定新的宽度,如果宽度是100%,并且padding.margin不为零,导致块级元素宽度溢出父元素 3.盒子模型的高度默认由内容决定 4.盒子模型中高度.宽度和内外边距都是可控制

修饰符-包-内部类-代码块执行顺序

1.访问权限修饰符     从大到小的顺序为:public--protected--default--private     private--只能在同一类中使用;     default--不用写出来,默认不加.可以被同一包中的类使用     protected--可以被不同包的子类使用     public--可以被不同包的其它类使用 2.各种修饰符的修饰对象(可修饰哪些:类/接口/方法/属性)(多个修饰符连用是可以没有顺序的!)     1)访问权限修饰符:public/default--

iOS学习之代码块(Block)

代码块(Block) (1)主要作用:将一段代码保存起来,在需要的地方调用即可. (2)全局变量在代码块中的使用: 全局变量可以在代码块中使用,同时也可以被改变,代码片段如下: 1 int local = 1;//注意:全局变量 2 void (^block0)(void) = ^(void){ 3 local ++; 4 NSLog(@"local = %d",local); 5 }; 6 block0(); 7 NSLog(@"外部 local = %d",lo

BZOJ 2730:[HNOI2012]矿场搭建(割点+连通块)

[HNOI2012]矿场搭建 Description 煤矿工地可以看成是由隧道连接挖煤点组成的无向图.为安全起见,希望在工地发生事故时所有挖煤点的工人都能有一条出路逃到救援出口处.于是矿主决定在某些挖煤点设立救援出口,使得无论哪一个挖煤点坍塌之后,其他挖煤点的工人都有一条道路通向救援出口.请写一个程序,用来计算至少需要设置几个救援出口,以及不同最少救援出口的设置方案总数.Input 输入文件有若干组数据,每组数据的第一行是一个正整数 N(N≤500),表示工地的隧道数,接下来的 N 行每行是用空

html中块元素的垂直水平居中。及兼容性

块在块中垂直居中(position:absolute; top:50%; margin-top:-二分之一高度) 块在块中水平居中 (子元素设置margin:0 auto 或auto) 用方法1将垂直水平同时居中 #box { width: 100px; height: 80px; background-color: gray; position:absolute; top:50%; left:50%; margin-top:-40px;<!--向上高度一半--> margin-left:-5

UVALive-7303- Aquarium【最小生成树】【连通块】

UVALive - 7303- Aquarium 题目链接:7303 题目大意:给你一个r * c的格子,每个格子有一个 ' \ ' 或者 '/' 的墙,以及打掉墙的费用,问使得所有块联通的最小费用.(看图好理解) 题目思路:就是将他化成一个图,联通的块相当于一个点,两个点之间有一条边,边的权值为墙的费用. 转化为连通块的思路是:每个格子看成两部分,左侧和右侧.以一行来看,假设两个格子A,B.那么B格子的右侧的编号一定和A格子的左侧的编号相同.如图所示 给每个格子的左右侧标上号,然后加入边,边的

ES6之块级作用域

一.前言 在ECMAScript6(以下简称ES6)之前,ECMAScript的作用域只有两种: 1.  全局作用域: 2.  函数作用域. 正是因为有这两种作用域,所以在JavaScript中出现一术语--“变量提升(hoisting)”. 如下: function func(){ console.log(test); var test = 1; }; func(); 在node环境执行上述代码,结果为: 之所以为’undefined’,原因就在于‘变量提升’,在进入func函数时,将所有通过

Smart210学习记录------块设备

转自:http://bbs.chinaunix.net/thread-2017377-1-1.html 本章的目的用尽可能最简单的方法写出一个能用的块设备驱动.所谓的能用,是指我们可以对这个驱动生成的块设备进行mkfs,mount和读写文件.为了尽可能简单,这个驱动的规模不是1000行,也不是500行,而是100行以内. 这里插一句,我们不打算在这里介绍如何写模块,理由是介绍的文章已经满天飞舞了.如果你能看得懂.并且成功地编译.运行了这段代码,我们认为你已经达到了本教程的入学资格,当然,如果你不

[Java基础]代码块

1. 2.构造代码块 public class CBlock { public CBlock()//构造函数 { i=300;//构造函数中初始化 } public int i=100;//显示初始化变量 {//构造代码块 i=200;//构造代码块中初始化变量 } } 从以下几点理解以上代码: java编译器编译一个java源文件的时候,会把成员变量的声明语句提前至一个类的最前端. 成员变量的初始化工作其实都在在构造函数中执行的. 一旦经过java编译器编译后,那么构造代码块的代码块就会被移动

Java(静态)变量和(静态)代码块的执行顺序

本文讨论Java中(静态)变量.(静态)代码块的执行顺序 首先创建3个类: 1.Foo类,用于打印变量 public class Foo { public Foo(String word) { System.out.println(word); } } 2.Parent类 public class Parent { static Foo FOO = new Foo("Parent's static parameter"); Foo foo = new Foo("Parent'