闭包与oc block(一)| 青训营笔记

132 阅读6分钟

这是我参与「第四届青训营 」笔记创作活动的第5天

闭包基础

基本定义

维基百科:

在计算机科学中,闭包(英语:Closure),又称词法闭包(Lexical Closure)或函数闭包(function closures),是在支持头等函数的编程语言中实现词法绑定的一种技术。闭包在实现上是一个结构体,它存储了一个函数(通常是其入口地址)和一个关联的环境(相当于一个符号查找表)。环境里是若干对符号和值的对应关系,它既要包括约束变量(该函数内部绑定的符号),也要包括自由变量(在函数外部定义但在函数内被引用),有些函数也可能没有自由变量。闭包跟函数最大的不同在于,当捕捉闭包的时候,它的自由变量会在捕捉时被确定,这样即便脱离了捕捉时的上下文,它也能照常运行。捕捉时对于值的处理可以是值拷贝,也可以是名称引用,这通常由语言设计者决定,也可能由用户自行指定

这么长的一段话,要配上图和解释才好 闭包.png 头等函数:编程语言中,函数可以当作函数的返回值、入参、变量
词法绑定:把相关的符号和变量值绑定在一起
约束变量:函数内部绑定的符号,内部声明的变量值,也可以是函数外部传入的参数(入参)【一定有】
自由变量:在函数外被定义,但在函数内被引用的,被闭包所捕获的【可以没有】
闭包与函数的区别:“当捕捉闭包的时候,它的自由变量会在捕捉时被确定”,虽然自由变量声明在外部,但在闭包声明的时候,会把相关的自由变量捕捉过来,可以脱离捕捉时的上下文被正常使用。
对捕捉的自由变量的处理方式:1.值拷贝;2.名称引用
函数的入口地址:函数的入口地址称为函数指针。在C++中,函数名是函数的入口地址

网上查的资料: 闭包是能够读取其他函数内部变量的函数,在js中,可以将闭包理解成“函数中的函数”。

引用:什么是闭包?(定义、特性)

网址:www.jianshu.com/p/e06b5e067…

闭包由两部分构成:函数(外部函数嵌套的内部函数)创建该函数的环境
函数内部可以直接读取全局变量,但是在函数外部无法读取函数内部的局部变量。

function f1(){
  var n=999;
  function f2(){
    alert(n); 
  }
}
var test = f1();// 外部函数调用之后其变量对象n本应该被销毁
test();// alert 999,但闭包阻止了它们的销毁,所以仍然可以访问外部函数的变量对象

函数f2被包括在函数f1内部,这时f1内部的所有局部变量,对f2都是可见的;但是f2内部的局部变量,对f1就是不可见的(父对象的所有变量,对子对象都是可见的,反之则不成立
只要把f2作为返回值,就可以在f1外部读取f1的内部变量,f2函数,就是闭包
本质:闭包是将函数内部和函数外部连接起来的桥梁

不同语言中的表现形式

c++ lambda表达式

c++中闭包表现形式就是lambda表达式
先看学习资料里的例子:

void abssort(float* x, unsigned n) {
    std::sort(x, x + n,
        // Lambda expression begins 
        [](float a, float b) {
            return (std::abs(a) < std::abs(b)); 
        } // end of lambda expression 
    ); 
}

作者:青训营官方账号
链接:https://juejin.cn/post/7123446183363608613/
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

由于没有c++基础,我就去查了查资料

引用:C++ lambda表达式

网址:blog.csdn.net/XXXTENTAC1O…

lambda 表达式 定义了一个匿名函数,并且可以捕获一定范围内的变量
语法形式:[caoture] (params) opt -> ret {body;};
capture是捕获列表;
params是参数表;
opt是函数选项;
ret是返回值类型;
body是函数体

在学习资料的例子中,是一个绝对值排序的函数,先调用了一个std::sort函数,在最后传入一个闭包。
闭包不捕获任何变量,有两个入参a,b,在{}中实现了判断ab哪个大
闭包的要素,有函数的入口地址,有约束变量(函数的入参),无自由变量
❓是不是说,闭包要有的外部函数嵌套的内部函数就是std::sort

Python

def make_multiplier_of(n): 
    def multiplier(x): 
        return x * n; 
    return multiplier;

Python中的闭包一般以函数内的嵌套函数来表示。
如果在一个内部函数里,对在外部作用域(但不是在全局作用域)的变量进行引用,那么内部函数就被认为是闭包。
外层传入参数n,内层用n实现n的函数,再把内层函数返回出去。
闭包的要素,有函数的入口地址,有约束变量(函数的入参),无自由变量

oc

oc中闭包的实现就是block

#import <Foundation/Foundation.h>

int main(int argc, const char * argv[]) { 
    @autoreleasepool { 
        int i = 1024; 
        void (^printBlock)(void) = ^{ 
            printf("%d\n", i); 
        }; 
        printBlock(); 
    } 
    return 0; 
}

作者:青训营官方账号
链接:https://juejin.cn/post/7123446183363608613/
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

此处捕获了外部传入的值i,并非作为入参传入的变量,是自由变量

block基础

block和函数相似
block是闭包在Objective-C中的实现
block可以接受参数也可以有返回值
block可以分配在栈和堆上,也可以是全局的。分配到栈上的块可以拷贝到堆中,同标准的Objective-C对象一样,具备引用计数

标准格式

苹果提供快捷键在实现文件的方法体中 敲 inl 直接回车,就可以出现一个block的基本块为啥我实现不了

returnType(^blockName)(parameters) = ^returnType(parameters){
    statements
}

returnType:返回值
^:在OC中^操作符是用来声明一个block变量,^也标示着一段block文字的开始
blockName:闭包名
parameters:输入参数
示例:

int (^sumBlock)(int a, int b) = ^int(int a, int b) { 
    return a + b; 
}; 
int sum = sumBlock(1, 1);

常用简写

省略写法

学习文档中提供了“有返回有入参”、“无返回无入参”两种省略写法,网上资料:
1,无参无返回: void (^myBlock)() = ^(){ ...... };(无参的话前面小括号可以省略,后面分号不能少).
2,有参无返回: void (^myBlock)(int,int) = ^(int a, int b){ .......};(有参数的话,'='号后面的形参名不能省).
3,无参有返回: int (^myBlock)() = ^{.......return....};
4,有参有返回:int (^myBlock)(int , int) = ^(int a ,int b){ ....return a+b;};
都省略了=后面的返回值类型

typedef

对于一些常见的block类型,会用typedef定义一些常用的名称
typedef returnType (^blockName)(parameters);
在返回值类型的左边加上typedef,之后就可以直接使用blockName了
示例:

typedef int (^SumBlock)(int a, int b); 
SumBlock block = ^(int a, int b) { 
    return a + b; 
};

首先定义了一个SumBlock类(用typedef声明的事类,命名一般与类的命名相同,首字母大写),入参为a,b。定义一个block对象,就可以直接使用了。

❓typedef是一个起别名的声明,所以示例中typedef声明给“返回值是int,入参为a,b”这样的闭包起别名为SumBlock

练习

两数相减的block: 两数相减的block.png

先交先交,剩下的下午看