美文网首页
swift 闭包与闭包表达式

swift 闭包与闭包表达式

作者: 三国韩信 | 来源:发表于2021-03-19 17:34 被阅读0次

闭包与闭包表达式

在swift里闭包大家都很熟悉,相当于oc中的block。闭包表达式又是啥?很多人把闭包表达式等同于闭包,认为是同一个东东,其实严格上来说,闭包和闭包表达式是不一样的

闭包表达式

1、什么是闭包表达式
闭包表达式其实就是函数,是对函数的另一种定义方式。在swift中,可以通过func定义一个函数,也可以通过闭包表达式定义一个函数。
闭包表达一个函数的格式是这样的:

{
    (参数列表)->返回值类型  in 
    函数体代码
}
// 普通func表达一个函数
func sum(_v1: Int, _v2: Int) -> Int {
    return v1+v2
}
//执行这个函数 
sum(1,2)

// 通过闭包表达式来表达一个函数
var fn = {
    (v1: Int, v2: Int) -> Int  in
    return v1 + v2
}
//执行这个闭包表达式 
fn(1,2)

2、闭包表达式的简写
闭包表达式更多的情况下是作为普通函数的参数来使用,比如下面这个函数,第三个入参就是一个函数fn

func exec(v1: Int, v2: Int, fn:(Int, Int) -> Int) {
    return fn(v1+v2)
}
// 这里exec 函数有3个参数,最后一个参数传递的是一个函数,用闭包表达式来表示第三个参数。

由于swift的编译器特别智能,闭包表达式可以简写,编译也能识别出来。比如上面的函数在调用的时候,就可以有如下的简写:

// 正常的闭包表达式写法
exec(v1:10, v2:20, fn:{
    (v1:Int, v2:Int) -> Int in 
    return v1 + v2 
})

// 可以把参数类型和返回值类型省略
exec(v1:10, v2:20, fn:{
    v1, v2 in return v1 + v2 
})

// 把return都省略
exec(v1:10, v2:20, fn:{
    v1, v2 in v1 + v2 
})

// 用$0和$1表示第一个入参和第二个入参
exec(v1:10, v2:20, fn: {$0 + $1}

// 甚至$0和$1都可以省略,只留一个+号
exec(v1:10, v2:20, fn: { + } 

注:虽然Swift的编译器很智能,当个人不建议写的很省略,可读性不是很好滴。

如果将一个很长的闭包表达式作为函数的最后一个实参,使用尾随闭包可以增强函数的可读性。尾随闭包是一个被书写在函数调用括号外面(后面)的闭包表达式。类似上面的例子中exec函数的最后一个参数是一个闭包表达式,那么可以用尾随闭包的方式,如下:

// 正常的闭包表达式写法
exec(v1:10, v2:20, fn:{
    (v1:Int, v2:Int) -> Int in 
    return v1 + v2 
})

// 尾随闭包的写法
exec(v1:10, v2:20) {
    (v1:Int, v2:Int) -> Int in 
    return v1 + v2 
}

// 尾随闭包的简写
exec(v1:10, v2:20) {
    $0 + $1 
}

如果闭包表达式是函数的唯一实参,而且使用了尾随闭包的语法,那就不需要在函数名后边写圆括号。

// 闭包表达式是函数的唯一实参
func exec(fn:(Int, Int) -> Int) {
    return fn(v1+v2)
}

// 正常的写法
exec(fn: {
    (v1,v2) in 
    return v1 + v2
})

// 精简的写法
exec(fn:{$0 + $1})

// 尾随闭包的写法
exec() {
  $0+$1
}

// 更精简的写法
exec { $0+$1 }
// 最后这种写法是表示一个函数只有一个参数,且执行这个函数使用了尾随闭包的表示方式。
闭包

1、定义
一个函数和它所捕获的变量\常量环境组合起来,称为闭包。一般指定义在函数内部的函数,一般它捕获的是外层函数的局部变量\常量;

typealias Fn = (Int) -> Int
func getFn () -> Fn {
    var num = 0
    func plus(_i: Int ) -> Int {
        num += i
        return num 
    }
    return plus
}

/* plus函数是定义在函数内且捕获了外部的变量num,整个plus函数就构成了一个闭包。
如果plus函数不捕获num变量的话,严格意义上也不算是闭包。*/

var fn1 = getFn()
fn1(1)  //1 
fn1(3)  //4 
fn1(5)  //9 

/*
从fn1的3次调用结果可以看出num在fn1里是一直存在的。
实际上num已经从原来的函数内的栈空间被移到堆空间了,而且一直被fn1 持有。
实际上整个fn1闭包16个字节,前8个字节是函数的地址,后8个字节是num的地址。
*/

可以把闭包想象成是一个类的实例对象;
内存在堆空间;
捕获的局部变量\常量就是对象的成员(存储属性);
组成闭包的函数就是类内部定义的方法;
class Closure {
    var num = 0 
    func plus(_i: Int) -> Int {
        num += i 
        return num 
    }
}

2、闭包的内存结构
闭包的内存结构结论: 16个字节,前8个字节是函数的地址(不是直接的地址,间接能找到的地址),后8个字节是对应捕获的变量的堆空间的地址。(以上面的getFn为例。)
3、闭包的循环引用
和oc中的block类似,在闭包中也会强引用着捕获的外部变量,如果闭包本身也被外部变量强引用着,那么此时也会构成循环引用。在实际开发过程中也要用[weak self]来打破循环引用。

class Student {
    var name: String?
    var giveAnswerClosure: ((Int) -> Void)?
    
    // 学生回答问题
    func giveAnswer() {
        // 调用闭包给出答案
        giveAnswerClosure?(1)
    }
    
    deinit {
        print("deinit---Student")
    }
}

class Teacher: NSObject {
    var student: Student?
    var isRight: Bool? // 答案是否正确
    
    override init() {
        super.init()
    
        student = Student()
        // 闭包回调
        student?.giveAnswerClosure = { answer in
            // 答案是1
            self.isRight = answer == 1 ? true : false
        }
    }
    
    // 提问问题
    func askQuestion() {
        // 学生回答
        student?.giveAnswer()
    }
    
    deinit {
        print("deinit---Teacher")
    }
}

class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
       
        let teacher = Teacher()
        teacher?.askQuestion()
    }
}

上面的例子中就存在着循环引用的情况,student持有者giveAnswerClosure这个闭包,而teacher由持有着student对象,在giveAnswerClosure闭包内又捕获了teacher对象(持有teacher)。这样就构成了teacher -> student -> giveAnswerClosure -> teacher的循环引用,导致teacher和student对象都不能释放。 打破循环引用的方式就是在闭包内用[weak self]。

// 写法一
student?.giveAnswerClosure = { [weak self] answer in
    self?.isRight = answer == 1 ? true : false
}

// 写法二
weak var weakSelf = self
student?.giveAnswerClosure = { answer in
    weakSelf?.isRight = answer == 1 ? true : false
}

更多的内存管理的请移步 https://www.jianshu.com/p/12688f7b9daf

相关文章

  • 闭包

    闭包 本节内容包括: 闭包表达式 尾随闭包 值捕获 闭包是引用类型 Swift 中的闭包与 C 和 Objecti...

  • Swift学习笔记(1)

    SWift学习笔记 闭包 闭包表达式 闭包是自包含的函数代码块,可以在代码中被传递和使用。Swift 中的闭包与 ...

  • swift 闭包与闭包表达式

    闭包与闭包表达式 在swift里闭包大家都很熟悉,相当于oc中的block。闭包表达式又是啥?很多人把闭包表达式等...

  • swift4 闭包

    swift 闭包 闭包:swift 中 函数是闭包的一种类似于oc的闭包闭包表达式(匿名函数) -- 能够捕获上下...

  • Swift语法 -- [07 - 闭包]

    在Swift中,可以通过func定义一个函数,也可以通过闭包表达式定义一个函数 1 闭包表达式 闭包表达式 闭包表...

  • Swift 5基础语法要点整理—闭包

    闭包 闭包表达式 在Swift中,可以通过func定义一个函数,也可以通过闭包表达式定义一个函数 闭包表达式的简写...

  • Swift-闭包

    闭包表达式 在swift中,可以通过func定义一个函数,也可以通过闭包表达式定义一个函数 闭包表达式简写 尾随闭...

  • 使用Playground快速练习Swift语法--闭包和枚举

    闭包 定义:闭包是自包含的函数代码块,可以在代码中被传递和使用。 闭包表达式语法 Swift闭包使用{}包含,in...

  • Day7 闭包(Closures)

    本页包含内容:• 闭包表达式• 尾随闭包• 值捕获• 闭包是引用类型• 逃逸闭包• 自动闭包 1、闭包表达式 闭包...

  • Swift--闭包

    闭包的概念 Swift闭包表达式 使用闭包返回值 使用尾随闭包 捕获上下文中的变量和常量 支持闭包有两个前提1、支...

网友评论

      本文标题:swift 闭包与闭包表达式

      本文链接:https://www.haomeiwen.com/subject/suabqltx.html