Swift闭包简单应用

语法表达式


{ (parameters) -> returnType in
    statements
}复制代码

这里的参数(parameters),能够是in-out(输入输出参数),但不能设定默认值。若是是可变参数,必须放在最后一位,否则编译器报错。元组也能够做为参数或者返回值。
"in"关键字表示闭包的参数和返回值类型定义已经完成,闭包函数体即将开始。即由in引入函数。api

闭包有三种形式:安全

  1. 全局函数是具备名称而且不捕获任何值得闭包
  2. 嵌套函数是具备名称并能够从其闭包函数捕获值得闭包
  3. 闭包表达式是以轻量级语法编写的未命名的闭包,能够从其定义的上下文中捕获并存储任何常量和变量的引用。

闭包表达式的优势:bash

  1. 从上下文中推导参数和返回值类型
  2. 单个表达式的闭包会隐式返回
  3. 速记参数名称
  4. 尾随闭包语法

简单实例:闭包

let sum = {(a: Int, b: Int) -> Int in
    return a + b
}
let s = sum(10, 15)
print(s)

// 从上下文中推导参数和返回值类型
let sum1 = { (a, b) -> Int in
    return a + b
}
print(sum1(25, 25))复制代码

概括
闭包类型是由参数类型和返回值类型决定的,和函数是同样的。闭包的类型是(parameters) -> returnTypeide

起别名

Swift使用typealias关键字能够为类型起一个别名函数

typealias block = (Int, Int) -> Int
let sum: block = {(a, b) in
    return a + b
}
print(sum(10, 15))复制代码

闭包表达式的用法,以sorted方法使用为例ui

// 尾随闭包
let num = [1, 2, 3, 4, 5, 6, 7]
let sortNum = num.sorted(by: {
    (a: Int, b: Int) -> Bool in
    return a > b
})
print("第一次排序: \(sortNum)")

// 从上下文中推导参数和返回值类型
let sortNum1 = sortNum.sorted(by: {
    (a, b) in
    return a < b
})
print("第二次排序: \(sortNum1)")

// 单表达式的闭包隐式返回,能够省略return关键字
let sortNum2 = sortNum1.sorted(by: {
    (a, b) in
    a > b
})
print("第三次次排序: \(sortNum2)")

// 速记参数名,和隐式返回。Swift提供速记参数名关联闭包参数(须要省略参数列表,并根据预期的函数类型推断速记参数名的数量和类型),从$0开始
let sortNum3 = sortNum2.sorted(by: {
    $0 < $1
})
print("第四次次排序: \(sortNum3)")

// sorted方法还可使用函数进行排序
func sortNumArr(a: Int, b: Int) -> Bool
{
    return a > b
}
let sortNum4 = sortNum3.sorted(by: sortNumArr)
print("第五次次排序: \(sortNum4)")

/*
输出结果
第一次排序: [7, 6, 5, 4, 3, 2, 1]
第二次排序: [1, 2, 3, 4, 5, 6, 7]
第三次次排序: [7, 6, 5, 4, 3, 2, 1]
第四次次排序: [1, 2, 3, 4, 5, 6, 7]
第五次次排序: [7, 6, 5, 4, 3, 2, 1]
*/复制代码

尾随闭包


sorted的使用,也能够将闭包写在括号以外。例如:spa

let num = [1, 2, 3, 4, 5, 6, 7]
let sortNum = num.sorted() {
    (a: Int, b: Int) -> Bool in
    return a > b
}
print("第一次排序: \(sortNum)")复制代码

若将闭包做为函数参数最后一个参数,能够省略参数标签,将闭包表达式写在函数调用括号后面指针

func sum(by: (Int, Int) -> Int) -> Int {
    return by(10, 20)
}
let num = sum() {
    (a, b) in
    return a + b
}
print("和为 \(num)")


func sum1(a: Int, b: Int, by: (Int, Int) -> Int) -> Int{
    return by(a, b)
}

let num1 = sum1(a: 10, b: 30) {
    (a, b) in
    return a + b
}
print("和为 \(num1)")复制代码

捕获常量和变量


闭包能够在其定义的上下文中捕获常量和变量。及时定义这些常量和变量的原始范围再也不存在了,闭包也能够引用并修改这些常量和变量的值。code

  • 嵌套函数捕获值
func sjzFunc(a: Int) -> () -> Int {
    var sjz = 0

    func addSjz() -> Int {
        sjz += a
        return sjz
    }

    addSjz()
    print("sjz的值为:\(sjz), a的值为:\(a)")

    return addSjz
}

let sjzF = sjzFunc(a: 100)
print("值为:\(sjzF())")
print("值为:\(sjzF())")
print("值为:\(sjzF())")
print("值为:\(sjzF())")

/*
sjz的值为:100, a的值为:100
值为:200
值为:300
值为:400
值为:500
*/复制代码
  • 闭包捕获值:
func sjzFunc(a: Int) -> () -> Int {
    var sjz = 0

    // 闭包
    let addSjz: () -> Int = {
        sjz += a
        return sjz
    }
    addSjz()
    print("sjz的值为:\(sjz), a的值为:\(a)")

    return addSjz
}

let sjzF = sjzFunc(a: 100)
print("值为:\(sjzF())")
print("值为:\(sjzF())")
print("值为:\(sjzF())")
print("值为:\(sjzF())")
/*
sjz的值为:100, a的值为:100
值为:200
值为:300
值为:400
值为:500
*/复制代码

能够看出,当调用addSjz的时候,sjz的值也改变了
addSjz捕获对变量sjz和参数a的引用,经过引用捕获确保sjzFunc结束时,sjz和a不会消失。而且确保了下次调用sjzF时,sjz可用。当它们再也不须要时,Swift处理变量的全部内存管理。

函数和闭包是引用类型。也就是说,当给变量和常量赋值闭包或函数时,变量和常量被赋值为闭包或函数的引用。
例如:

let sjzFF = sjzF // sjzFF和sjzF引用的是同一个闭包复制代码

逃避闭包


当闭包做为参数传递给函数时,闭包被称为转义函数,在函数返回后调用。
声明一个将闭包做为一个参数的函数时,能够在参数类型以前编写@escaping,以指示容许闭包被转义。

闭包能够逃避的方法之一是存储在函数外定义的变量中。

var sjzBlock: () -> Void = {

}

func someEscapingFunc(com: @escaping () -> Void){
    print("执行函数")
    sjzBlock = com
}

func someNoneFunc(com: () -> Void){
    com()
}

class someClass {
    var x = 10
    func doSomeThint() -> Void {
        someEscapingFunc {
            self.x = 20
        }

        someNoneFunc {
            x = 30
        }
    }
}

let some = someClass()
some.doSomeThint()
print(some.x)

sjzBlock()
print(some.x)

/*
输出
执行函数
30
20
*/复制代码

用@escaping标记一个闭包意味着你必须在闭包中self引用。 例如,someEscapingFunc的闭包是一个转义闭包,这意味着它须要self引用。 相反,封闭传递给someNoneFunc是一个非转义的闭包,这意味着它能够隐式引用。

@escaping和@non-escaping修饰符是用来修饰闭包的。闭包默认为@non-escaping类型
@escaping:转义闭包,闭包的生命周期不在传入的函数范围内管理。由函数外部变量持有。当函数结束以后,闭包才去执行
@non-escaping:闭包在函数内执行完后,函数才去执行,闭包销毁。
当使用@escaping时,要考虑self被循环引用。

循环引用


缘由参考OC的block,解决方法也相似。

Dog类

import Cocoa

class sjzDog: NSObject {
    var eatFull: ((String) -> Void) = {(String) in

    }
    var food: String?

    override init() {
        super.init()
    }

    deinit {
        print("狗挂了")
    }

    func eat(food: String, by: @escaping (String) -> Void) -> Void {
        self.food = food
        print("我吃了:\(food)")
        eatFull = by
    }

    func eatFinish() -> Void {
        eatFull(food!)
    }
}复制代码
People类

import Cocoa

class sjzPeople: NSObject {
    let dog: sjzDog
    var name: String?

    init(name: String) {
        self.name = name
        dog = sjzDog()

        super.init()
    }

    deinit {
        print("人挂了")
    }

    func feed(food: String) -> Void {
//        循环应用
//        输出:
//        我吃了:milk
//        我吃了milk, 我吃饱了,我想和主人Tom玩耍
//        dog.eat(food: food) { (a) in
//            print("我吃了\(a), 我吃饱了,我想和主人\(self.name!)玩耍")
//        }


        /*如下输出:
             我吃了:milk
             我吃了milk, 我吃饱了,我想和主人Tom玩耍
             人挂了
             狗挂了
         */
//        1. 解决循环引用
//        weak var weakSelf = self
//        dog.eat(food: food) { (a) in
//            print("我吃了\(a), 我吃饱了,我想和主人\(weakSelf!.name!)玩耍")
//        }

//        2. 解决循环引用
//        dog.eat(food: food) { [unowned self] (a) in
//            print("我吃了\(a), 我吃饱了,我想和主人\(self.name!)玩耍")
//        }

//        3. 解决循环引用
        dog.eat(food: food) { [weak self] (a) in
            print("我吃了\(a), 我吃饱了,我想和主人\(self!.name!)玩耍")
        }

        dog.eatFinish()
    }
}复制代码
调用

var people: sjzPeople? = sjzPeople.init(name: "Tom")
people!.feed(food: "milk")
people = nil复制代码

循环引用缘由:
dog类中引用了闭包eatFull,people类中引用了dog类,闭包eatFull又引用了people,造成了闭环

在dog类中继续声明变量和方法:

var play: (() -> Void)?

func playOrFood() -> Void {
        play = {
            print("我要玩, 我不想吃 \(String(describing: self!.food))")
        }

        play!()
    }复制代码

在调用playOrFood方法后,将dog对象置空,dog对象不能释放,造成了循环引用
缘由:
dog类引用了paly闭包,paly闭包又引用了dog类。解决方法和上面dog类和people互相引用相似

解决方法:

  1. weak var weakSelf = self,使用weakSelf调用属性
  2. 在闭包{和参数之间使用[unowned self]
  3. 在闭包{和参数之间使用[weak self]

weak表示可用范围内self为弱引用unowned表示可用范围内self都为assign,不会强引用,若是对象释放,指针地址依然存在,不安全

相关文章
相关标签/搜索