Objective-c中的Block(块)详解

Block初探

在Objective-c中NSArray是很常用的容器之一,很多时候我们需要对数组中的数据进行排序,因此与下面类似的代码会经常碰到:

NSArray *sortedArray = [array sortedArrayUsingComparator: ^(id obj1, id obj2) {

    if ([obj1 integerValue] > [obj2 integerValue]) {
        return (NSComparisonResult)NSOrderedDescending;
    }

    if ([obj1 integerValue] < [obj2 integerValue]) {
        return (NSComparisonResult)NSOrderedAscending;
    }
    return (NSComparisonResult)NSOrderedSame;
}];

刚接触objc的朋友可能会对这段代码表示看不懂。

sortedArrayUsingComparator: ^(id obj1, id obj2) {
     // 代码省略
}];

这个以^开头的参数类型为NSComparetor,原型如下 :

typedef NSComparisonResult (^NSComparator)(id obj1, id obj2); 

其实这个Comparetor的功能就类似于C语言中的函数指针,以及C++11、Java 8 、C#等语言中的Lambda表达式。

C语言中的函数指针

我们首先来回顾一下C语言中的最简单的函数指针,在C语言中我们以

returntype (*name) (arg);

的格式定义一个函数指针变量。或者以 typedef的形式定义一个函数指针类型。示例代码如下 :

// C语言的函数指针声明
int (*addInC)(int , int ) ;

// 将此类型的函数指针声明为一个AddOperation类型
typedef int (*AddOperation)(int, int);

// 普通C语言函数
int doAdd(int a, int b) {
    return a + b ;
}

//
int main(int argc, const char * argv[])
{

    @autoreleasepool {
        // addInC指针变量指向doAdd函数
        addInC = doAdd;
        NSLog(@" addInC : %i.", addInC(5, 6)) ;	// 函数指针调用
        // 声明一个aop函数指针变量,也指向doAdd
        AddOperation aop = doAdd ;
        NSLog(@" AddOperation : %i.", aop(100, 23) );
    }
    return 0;
}

输出 :

addInC : 11.
AddOperation : 123.

首先是生命一个函数指针,addInC,然后在main函数中将addInC指向doAdd函数,这样调用addInC(5, 6)就等于调用了doAdd(5, 6)。同理AddOperation也是这个原理。Java
8中的Lambda表达式

Objc中的块

现在我们回到Objc中的Block, 就是上面NSArray中排序用到的块。我们看看官方的定义 :

Blocks are a language-level feature added to C, Objective-C and C++, which allow you to create distinct segments of code that can be passed around to methods or functions as if they were values. Blocks are Objective-C objects, which means they can be added to collections like NSArray or NSDictionary. They also have the ability to capture values from the enclosing scope, making them similar to closures or lambdas in other programming languages.

最后一句明确说明了块类似于其他语言中的lambda以及闭包。

我们看看块的声明格式 :

returnType (^name) (arguments) 

其中returnType代表返回值类型,name代表变量名,argments代表参数列表,如果没有参数可以写成(void)或者()。

再看看块的定义格式 :

^ [returnType] (arguments) {  statements };

其中returnType是可选的,arguments是参数列表,statements是实现功能的代码。我们看如下示例 :

// 有返回值, 但是在定义块函数时返回值可选.
int (^addUseBlock) (int, int) = ^/* int*/ (int a, int b ) { return a + b ; };

我们声明了addUseBlock变量,然后让其指向一个有两个int参数的块。在定义时返回类型int可以省略。调用代码如下:

        // 使用Block函数
        NSLog(@" add use Blockm, Result ==> %i.", addUseBlock(3,4) );

最终会返回 3 + 4的结果。

将块定义为一种类型

我们可以想C语言中的函数指针一样,使用typedef将块定义为一种类型,这样便于将块当做函数传递。下面我们定义一个Comparetor类型,有一个int返回值,有两个int行参数,示例如下:

// 定义一个Compare类型
typedef int (^Comparetor) (int arg1, int arg2) ;

这个看起来就很像NSArray中的NSComparator了,只是返回的类型不同而已。

这个时候我们使用块来当做参数就容易多了,如下示例 :

// Block当做参数
int helpCompare(Comparetor cmp ) {
    return cmp(3, 4);
}

// 返回一个Block对象
Comparetor getComparetor(){
    return ^(int a, int b) { return a > b ? -1 : 1; };
}

//
int main(int argc, const char * argv[])
{

    @autoreleasepool {

        // 使用Comparetor类型定义Block
        Comparetor cmp = ^(int a, int b) {
            if ( a == b ) {
                return 0 ;
            }
            return a > b ? 1 : -1 ;
        } ;

        NSLog(@"use Comparetor, Result ==> %i.", cmp(3,4) );
        // 使用Comparetor类型的cmp当做参数
        NSLog(@"use Comparetor, Result ==> %i.", helpCompare(cmp) );

        // 使用Comparetor类型的匿名函数当做参数, 该匿名block固定返回123
        NSLog(@"use Comparetor, Result ==> %i.", helpCompare( ^(int a, int b) {return 123; } ) );
        // 通过函数返回一个Block, 并且把它当做参数传递给helpCompare
        NSLog(@"use Comparetor, Result ==> %i.", helpCompare( getComparetor() ) );

    }
    return 0;
}

输出如下 :

use Comparetor, Result ==> -1.
use Comparetor, Result ==> -1.
use Comparetor, Result ==> 123.
use Comparetor, Result ==> 1.

块使用上下文变量

块代码也可以使用其所在范围的变量,但是块代码中不能修改该变量的值或者引用, 例如块所在函数的变量、类的成员变量。如果需要在块代码中修改变量值,可以使用__block定义变量。示例如下 :

int main(int argc, const char * argv[])
{

    @autoreleasepool {
        // 使用上下文变量
        int valueInMain = 222 ;

        void (^printValue)() = ^() {
            NSLog(@"使用函数上下文的变量(不能修改) : %i.", valueInMain) ;
        } ;
        printValue();

        __block int anInteger = 42;
        void (^testBlock)(void) = ^{
            anInteger = 100;
            NSLog(@"使用__block声明的变量(可以修改) : %i", anInteger);
        };
        testBlock();

    }
    return 0;
}

输出如下 :

使用函数上下文的变量(不能修改) : 222.
使用__block声明的变量(可以修改) : 100

Objective-c中的Block(块)详解,布布扣,bubuko.com

时间: 2024-09-29 06:19:40

Objective-c中的Block(块)详解的相关文章

java中static{}语句块详解

1.当一个类中有多个static{}的时候,按照static{}的定义顺序,从前往后执行: 2.先执行完static{}语句块的内容,才会执行调用语句: 示例二 public class TestStatic{    static{        System.out.println(1);    }    static {        System.out.println(2);    }    static {        System.out.println(3);    }    p

Java中的main()方法详解

在Java中,main()方法是Java应用程序的入口方法,也就是说,程序在运行的时候,第一个执行的方法就是main()方法,这个方法和其他的方法有很大的不同,比如方法的名字必须是main,方法必须是public static void 类型的,方法必须接收一个字符串数组的参数等等. 在看Java中的main()方法之前,先看一个最简单的Java应用程序HelloWorld,我将通过这个例子说明Java类中main()方法的奥秘,程序的代码如下: 1 /** 2 * Java中的main()方法

PHP中的ob_start用法详解

用PHP的ob_start();控制您的浏览器cache Output Control 函数可以让你自由控制脚本中数据的输出.它非常地有用,特别是对于:当你想在数据已经输出后,再输出文件头的情况.输出控制函数不对使用 header() 或 setcookie(), 发送的文件头信息产生影响,只对那些类似于 echo() 和 PHP 代码的数据块有作用.我们先举一个简单的例子,让大家对Output Control有一个大致的印象:Example 1. 程序代码 程序代码<?phpob_start(

图像处理中的数学原理详解21——PCA实例与图像编码

欢迎关注我的博客专栏"图像处理中的数学原理详解" 全文目录请见 图像处理中的数学原理详解(总纲) http://blog.csdn.net/baimafujinji/article/details/48467225 图像处理中的数学原理详解(已发布的部分链接整理) http://blog.csdn.net/baimafujinji/article/details/48751037 如果你对PCA的推导和概念还不是很清楚,建议阅读本文的前导文章 http://blog.csdn.net/

c++中vector的用法详解

c++中vector的用法详解 vector(向量): C++中的一种数据结构,确切的说是一个类.它相当于一个动态的数组,当程序员无法知道自己需要的数组的规模多大时,用其来解决问题可以达到最大节约空间的目的. 用法: 1.文件包含: 首先在程序开头处加上#include<vector>以包含所需要的类文件vector 还有一定要加上using namespace std; 2.变量声明: 2.1 例:声明一个int向量以替代一维的数组:vector <int> a;(等于声明了一个

Swift 中的Closures(闭包)详解

Swift 中的Closures(闭包)详解 在Swift没有发布之前,所有人使用OC语言编写Cocoa上的程序,而其中经常被人们讨论的其中之一 -- Block 一直备受大家的喜爱.在Swift中,同样有这样的一个角色,用于当开发者需要异步执行的之后使用的一种语法 - Closure.中文翻译为闭包. 闭包出了可以进行异步执行之外,它的完整使用还依赖闭包本身的变量.常量的捕获.闭包捕获并存储对它们定义的上下文中的任何常量和变量的引用,这也就意味着,你可以在任何时候异步执行闭包的时候获取之前的所

oracle中的dual表详解

oracle中的dual表详解 1.DUAL表的用途 Dual 是 Oracle中的一个实际存在的表,任何用户均可读取,常用在没有目标表的Select语句块中 --查看当前连接用户 SQL> select user from dual; USER ------------------------------ SYSTEM --查看当前日期.时间 SQL> select sysdate from dual; SYSDATE ----------- 2007-1-24 1 SQL> sele

(转)linux 中特殊符号用法详解

linux 中特殊符号用法详解 原文:https://www.cnblogs.com/lidabo/p/4323979.html # 井号 (comments)#管理员  $普通用户 脚本中 #!/bin/bash   #!/bin/sh井号也常出现在一行的开头,或者位于完整指令之后,这类情况表示符号后面的是注解文字,不会被执行. # This line is comments.echo "a = $a" # a = 0由于这个特性,当临时不想执行某行指令时,只需在该行开头加上 # 就

图像处理中的数学原理详解17——卷积定理及其证明

欢迎关注我的博客专栏"图像处理中的数学原理详解" 全文目录请见 图像处理中的数学原理详解(总纲) http://blog.csdn.net/baimafujinji/article/details/48467225 图像处理中的数学原理详解(已发布的部分链接整理) http://blog.csdn.net/baimafujinji/article/details/48751037 1.4.5   卷积定理及其证明 卷积定理是傅立叶变换满足的一个重要性质.卷积定理指出,函数卷积的傅立叶变