关闭

OC高级特性--Block匿名函数

标签: 闭包blockoc内存匿名
1204人阅读 评论(0) 收藏 举报
分类:

块是Objective-C语言提供的一个强大特性,博主会介绍块语法的意义、块内存管理、怎样在程序中开发块和怎样使用现有API(如Foundation框架)中的块。
简言之,块提供了一种方式,使用这种方式可以创建一组语句(即代码块)并将这些语句赋予一个变量,随后就可以调用这个变量。从这方面看,块与函数方法类似,但除了是可执行代码外,块还含有与堆内存和栈内存绑定的变量。块就是一个实现的闭包(closure),一个允许访问其常规范围之外变量的函数。此外,一个Objective-C块实际上就是一个对象;一个NSObject类的子类,拥有NSObject类的所有相关属性(如可以向块发送消息)。

1.块的语法

在学习使用块编写的过程中,难点之一是掌握块的语法。本段内容详解介绍块语法,并且会通过示例展示块在代码中的作用。
块类型由返回值类型和参数类型列表构成。使用脱字符(^)可以声明块类型的变量。
声明块类型的语法

也许这张图片看客们还不能让各位看客去和C语言的函数做类比记忆,博主带着大家将Block的定义和函数指针类型做对比记忆。

//1.sum函数声明
函数声明:int sum(int a, int b);
函数类型int(int, int);
函数指针类型:int(*)(int, int);
将函数指针类型重命名:typedef int(*FUNP)(int, int);
//2.Block声明
注意:这里只要将函数指针类型标志"*"改为Block语法的标志"^"就是Block语法。
int (^sum)(int, int)
上面博主已经介绍过了,Block是将一个函数的实现赋予Block变量,下面定义函数。
//3.函数定义语法
int sum(int a, int b) {
    return a + b;
}
//4.Block定义
把前面的Block变量类型拿过来
int (^sum)(int, int)
将函数的定义赋给Block变量sum
int (^sum)(int, int) = ^int (int a, int b) {
    return a + b;
}

注意:
1.后面的函数实现的名字被去掉了,这里赋给Block变量的其实是一个匿名函数。
2.等号右面加了一个Block语法标志”^”不能省略。
这样一个Block语法块、或者叫做匿名函数就创建好了


1.块常量表达式—-没有设置返回值类型的块常量表达式

^int (int addend) {
retutn addend + 1;
}

无需再块定义中为这个块设置返回值类型,因为编译器会从块主题中的return语句推断出返回值的数据类型。
2.不带参数的块常量定义

^ {
NSLog(@"Hello,World!");
}

块语法元素的比较

块语法元素 块变量声明 块常量表达式
脱字符 标识一个块变量声明的开始。脱字符位于变量名称之前,两者都被封装在圆括号中 标识一个块变量表达式的开始
名称 块变量的名称是必选项 块常量表达式没有名称
返回值类型 在块变量声明中返回值类型是必选项。没有返回值的块变量会将返回值声明为void 从块表达式的语句主题推断出返回值类型。如果块表达式的语句主题中有一个以上的return语句,那么他们的返回值必须为同一类
参数 在块变量声明中,参数类型列表是必选项。如果块变量没有参数,必须将参数类型列表声明为void 在块常量表达式中,参数列表是可选项

2.块就是闭包

如前面所述,块就是一个实现的闭包,就是一个允许访问在其常规范围外部声明的局部变量的函数。为了理解这些概念,让我们先来了解范围和可见性规则。变量的可见性是指变量在程序的哪个(些)部分中可以被访问,这也称为变量的范围。例如,在C语言函数定义中声明的变量拥有局部范围,这意味着这些变量仅仅在该函数中是可见的和可访问的(注意,函数还可以引用全局变量)。与C语言函数相比,块参数通过以下特性提高了变量的可见性。

  • 对词汇范围的支持:这是从封闭范围引用局部变量和参数的特性。
  • __block变量:__block关键字可以应用于块外部但仍处于同一词汇范围外的变量。通过该特性可以修改块内部的变量。
  • 访问实例变量:在对象的方法实现代码中定义的块可以访问该对象中的实例变量。
  • 导入参数 :通过头文件(通过#improt或#include指令)导入的常数变量在块常量表达式中是可见的。

2.1词汇范围

块的一大特性就是支持词汇范围。与之相反,C语言函数无法访问在其定义外部声明的局部变量。

void logValue() {
//错误,对变量myVar进行了非法访问,该变量不在访问范围内
    NSLog(@"Variable value %d", myVar);
}
int main(int argc, const char *argv[]) {
    int myVar = 10;
    logValue();

    return 0;
}

块支持词汇范围,因此块常量表达式可以访问在同一词汇范围内声明的变量。此外,一方面,能够定义变量的地方就能够定义块,例如,在函数、方法和其他块中都可以定义块。另一方面、C语言函数无法在其他函数和方法中定义。以下代码可以成功编译和运行,因为变量myVar是在logValueBlock块所在的词汇范围内声明的。

{
    int myVar = 10;
    void (^logValueBlock)(void) = ^{
        NSLog(@"Variable value = %d", myVar);
    };
    logValueBlock();
}

如以上代码所示,一个块访问了在其定义之外声明的局部变量。尤其是,这些局部变量是在声明块常量表达式之前,在一个封闭范围内被声明(和初始化)的。可以用花括号划定局部范围,此外,还可以嵌套捍卫。一方面,如上面代码所示,变量myVar是定义logValueBlock块的范围内被声明的,而且其声明位于块常量表达式之前,因为可以在这个块常量表达式中使用。另一方面,如果局部变量myVar是在块常量表达式之后声明和初始化的,则块对局部变量的访问是非法的。

{
    int myVar = 10;
    void (^logValueBlock)(void) = ^{
        //错误,词汇范围变量无法重新赋值
        myVar = 5;
        NSLog(@"Variable value = %d", myVar);
    };
    logValueBlock();
} 

2.2可以修改的__block变量

默认情况下,在块常量表达式中通过词汇范围访问的块局部变量不能修改。使用存储类型修改符__block可以将这些变量切换为读写模式(可以修改)。除了C语言的变长数组(不是由常量表达式表示长度的数组)和含有变长数组的C语言结构之外,可以对Objective-C支持的所有类型使用__block修改符。__block修改符不能与局部变量存储修改符auto、register和static组合使用。

__block int myVar = 10;
void(^incBlock)(int) = ^(int amount) {
    myVar += amount;
    NSLog(@"New value = %d", myVar);
};
incBlock(5);

当引用变量的块被赋值到堆存储区域时,使用__block修饰符的变量也会被复制到堆存储区域。这个本博客最后的知识点因除了内存管理的话题,下面将介绍该内容。

0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:3464次
    • 积分:174
    • 等级:
    • 排名:千里之外
    • 原创:10篇
    • 转载:0篇
    • 译文:0篇
    • 评论:4条
    文章分类
    文章存档
    最新评论