swift-05-函数、闭包

885 阅读9分钟

最近在学习swift,期间也看了很多教程和大神的博客,最后把笔记放这里,方便查看复习~

附上学习资料地址:w3cschool-Swift 教程YungFan大神的简书(推荐看一下,有些知识点总结地简直通俗易懂)

一、函数

1、介绍

函数就是oc中的方法

常规格式:

func 函数名(参数列表) -> 返回值类型 {
    代码块
    return 返回值
}

2、多种情况下的格式

1、无参+无返回值

//常规写法
func about() -> Void {
    print("iPhone Xs Max")
}
// 调用函数
about()

// 简单写法
// 如果没有返回值,Void可以写成()
func about1() -> () {
    print("iPhone Xs Max")
}
about1()

// 如果没有返回值,后面的内容可以都不写
func about2() {
    print("iPhone Xs Max")
}

about2()

2、无参+有返回值

func readMessage() -> String {
    return "吃饭了吗?"
}
var str = readMessage()
print(str)

3、有参+无返回值

func call(phoneNum : String) {
    print("打电话给\(phoneNum)")
}
call("18888888888")

4、有参+有返回值

func sum(num1 : Int, num2 : Int) -> Int {
    return num1 + num2
}
var result = sum(num1: 20, num2: 30)
print(result)

5、返回值为复杂类型

//大致意思为,返回值是一个元组类型
func getInfo(info:String) -> (name:String, age:Int) {

    let infos = info.components(separatedBy: ",")

    //注意,第二个类型要转换一下,并且加!
    return (infos[0], Int(infos[1])!)

}

let person:(name:String, age:Int) = getInfo(info: "zhangsan,20")

print(person.name)
print(person.age)

3、注意事项

1、函数的参数是常量;

2、形参标签+形参名称,也就是:外部参数名+内部参数名+类型;

函数参数的常规写法

func addFunc(num1 a: Int,num2 b: Int) -> Int {
    return a + b
}

let result = addFunc(num1: 5, num2: 5)

print(result) //10

简写,省略

func addFunc(_ a: Int,_ b: Int) -> Int {
    return a + b
}

let result = addFunc(5, 5)

print(result) //10

3、可以设置默认参数(在没有传入参数的情况下);

func makecoffee(type :String = "冰美式") -> String {
    return "来一杯\(type)。"
}

let coffee1 = makecoffee(type: "拿铁")
print(coffee1) //来一杯拿铁。

let coffee2 = makecoffee()
print(coffee2) //来一杯冰美式。

4、可变参数:swift中接受参数的个数不确定,但类型要相同;

func total(numbers:Int...) -> Int {
    var sum = 0
    for i in numbers {
        sum += i
    }
    return sum
}

let res1 = total()
let res2 = total(numbers:10)
let res3 = total(numbers:10,20)
let res4 = total(numbers:10,20,30)

print(res1) //0
print(res2) //10
print(res3) //30
print(res4) //60

5、inout引用类型:一般参数只是传递一个值进来,如果想改变外部参数就要用到inout,类似block中的__block;

func changeInt(a: inout Int, b: inout Int) {

    a += 12
    b += 100
}

var a = 10
var b = 20

print("调用前:a=\(a), b=\(b)") 
//打印:调用前:a=10, b=20

changeInt(a: &a, b: &b)

print("调用后:a=\(a), b=\(b)") 
//打印:调用后:a=22, b=120

6、函数的嵌套:内嵌的函数只能在该函数里面调用

4、函数的类型及嵌套函数

1、函数的引用类型 2、每个函数都有自己的类型 3、定义+使用->代码示例 4、嵌套函数(一个函数作为另一个函数的参数 、一个函数作为另一个函数的返回值)

函数类型的使用:

//定义一个函数
func addTwoInts(a : Int, b : Int) -> Int {
    return a + b
}

//这个函数的类型是:(Int, Int) -> Int
//于是,我们可以这么使用:
var mathFunction : (Int, Int) -> Int = addTwoInts

//然后可以这么调用
let res = mathFunction(10, 20)
print(res)

一个函数作为另一个函数的参数:

//定义一个函数
func addTwoInts(a : Int, b : Int) -> Int {
    return a + b
}

//这个函数的类型是:(Int, Int) -> Int
//作为一个参数传入另一个函数
//参数名: 函数类型

// 将函数的类型作为函数的参数
func printResult(a : Int, b : Int, calculateMethod : (Int, Int) -> Int) {
    print(calculateMethod(a, b))
}

//调用自定义函数
printResult(a: 10, b: 20, calculateMethod: addTwoInts)

//也可以调用 手写一个闭包穿进去
printResult(a: 12, b: 22, calculateMethod: {(m: Int, n: Int) -> Int in return m + n})

一个函数作为另一个函数的返回值:

//定义两个函数
func addTwoInts(a : Int, b : Int) -> Int {
    return a + b
}

func multiplyTwoInt(a : Int, b : Int) -> Int {
    return a * b
}

//函数作为返回值
func getResult(a:Int) -> (Int, Int)->Int{
    if a > 10 {
        return addTwoInts
    }
    else{
        return multiplyTwoInt
    }
}
//调用返回的函数
let res1 = getResult(a: 2)(10,20)
print(res1) //200

let res2 = getResult(a: 12)(10,20)
print(res2) //30

二、闭包

1、介绍

闭包:是一个可以被传递、引用的独立模块;

它能够捕捉、存储 定义在它上下文中的任何常量和变量,也就是说:闭合并包裹 其里面的常量和变量,因此叫做:闭包

闭包和函数一样,都是引用类型

分为三种形式:

  • 全局函数,是一个有名字但不会捕捉任何值的闭包
  • 内嵌函数,是一个有名字且可以从上层函数捕捉值的闭包
  • 闭包表达式,是一个轻量级语法,可以捕捉其上下文中的常量和变量值的没有名字的闭包

2、与函数比较

计算一个数的特定值,比如平方

函数的写法

func square(num: Int)  -> Int{
    return num * num
}

//调用
square(num: 5)

闭包的写法

let squareCloure = { (num: Int) -> Int in
    return num * num
}
//调用
squareCloure(6)
实战场景

条件


//这是一个函数,它里面有一个参数 需要传入一个函数/闭包
//从数组中筛选指出合适的数据组成新的数组
func getList(score:[Int], con:(Int)->Bool) -> [Int]{

    var newScore:[Int] = [Int]()
    for item in score {
        //把每一个元素传入con方法,根据返回值判断是否执行{}
        if con(item) {
            newScore.append(item)
        }
    }
    return newScore
}

//已知数组
let scoreArray = [75,60,95,45,85]

函数方式处理

//函数的方式传参,先声明一个函数
func method(num: Int) -> Bool{
    return num > 50
}

let newArrayM = getList(score: scoreArray, con: method)
print(newArrayM) //[75, 60, 95, 85]

闭包方式处理

//闭包方式传参,写法1
let closure = {(num: Int) -> Bool in
    return num > 50
}

let newArrayC = getList(score: scoreArray, con: closure)
print(newArrayC) //[75, 60, 95, 85]




//闭包方式传参,写法2
let newArrayC2 = getList(score: scoreArray,  con:{(num: Int) -> Bool in return num > 50})

print(newArrayC2) //[75, 60, 95, 85]

3、主要知识点

1、由in关键字,将闭包分成两个部分:参数与返回值、闭包体 2、与函数不同的一点:参数不能设置默认值,其他与函数一样 3、参数名称有缩写功能,通过$0、$1、$2依次进行参数的调用 4、可以根据不同情况进行简写:

  • 1、省略 ->返回值类型 (根据后面表达式可以推断返回值是一个Bool)
let newArrayC2 = getList(score: scoreArray,  con:{(num: Int) in return num > 50})
  • 2、省略 参数类型括号 (根据函数的参数可推断传进来的必然是Int)
let newArrayC2 = getList(score: scoreArray,  con:{num in return num > 50})
  • 3、省略return (因为是单行语句,可以省略return)
let newArrayC2 = getList(score: scoreArray,  con:{num in num > 50})
  • 4、通过参数名称缩写,省略 参数声明in
let newArrayC2 = getList(score: scoreArray,  con:{$0 > 50})

4、捕获

  • 闭包可以从上下文环境中捕获常量、变量,并在自己的作用域内使用

  • Swift最简单的闭包形式是嵌套函数,也就是定义在其他函数的函数体内的函数,嵌套函数可以捕获其外部函数所有的参数以及定义的常量和变量

//这里是一个嵌套函数,也可以看成 返回值是一个闭包
func makeIncrementor(forIncrement amount: Int) -> () -> Int {
    var runningTotal = 0
    func incrementor() -> Int {
    	//在闭包里可以捕获到本方法外的变量
        runningTotal += amount
        return runningTotal
    }
    return incrementor
}

let incrementByTen = makeIncrementor(forIncrement: 10)

print(incrementByTen())// 返回的值为10

5、尾随闭包

按个人理解就是为了增加可读性,闭包的写法改了个格式

条件:有一个函数,闭包是这个函数的最后一个参数 做法:省略闭包的形参,并把小括号提前,最后使用{}进行闭包的传递 好处:增加可读性

示例:

//函数,同时 闭包是最后一个参数
func doSomething(info:String, clousre:(String)->Void){
     clousre(info)
}

//正常调用
doSomething(info: "Hello", clousre: { str in print(str) })

//使用尾随闭包进行函数调用
doSomething(info: "World") { str in
    print(str)
}
        
        

6、逃逸闭包

使用@escaping 放在闭包形参类型之前,表明这是一个逃逸闭包,这个闭包会在 函数结束之后被调用。

用途:逃逸闭包常用于异步回调

//声明一个变量用于测试
var x = 10

//声明一个存放闭包的数组
var closureArray :[()->Void] = [()->Void]()


//为了对比,先定义一个函数,参数是一个正常闭包
func normolClosure(closure:()->Void){
    closure()
}
//再定义一个函数,参数是一个逃逸闭包
//把闭包 存储到 闭包数组中,但没有调用。等最后调用数组中的闭包
func escapeClosure(closure: @escaping ()->Void){
    print("函数开始")
    closureArray.append(closure)
    print("函数结束")
}


print("调用函数前,检测 x = \(x)")

/*
 调用正常闭包函数
 由于闭包是最后一个参数,可以增加可读性,使用尾随闭包
 */
normolClosure {
    x = 100
}
//打印100 因为闭包在函数里面执行了
print("调用正常闭包函数后,检测 x = \(x)")

//调用逃逸闭包函数
escapeClosure {
    x = 200
}

//此时还是100,因为没有调用 逃逸闭包
print("调用逃逸闭包函数后,检测 x = \(x)")


//在函数外面调用逃逸闭包
closureArray.first?()

print("函数外面调用逃逸闭包,检测 x = \(x)")



/*
 打印:
 调用函数前,检测 x = 10
 调用正常闭包函数后,检测 x = 100
 函数开始
 函数结束
 调用逃逸闭包函数后,检测 x = 100
 函数外面调用逃逸闭包,检测 x = 200
 */

7、自动闭包

使用@autoclosure 放在闭包形参类型之前,表明这是一个自动闭包。

用于包装函数参数的表达式,不接受任何参数,被调用时会返回被包装在其中的表达式的值

//自动闭包
func printIfTrue(predicate:@autoclosure ()->Bool){  
    if predicate() {
        print("is true")   
    }    
    else{       
        print("is false")       
    }   
}

//直接进行调用了,Swift 将会把 2 > 1 这个表达式自动转换为 () -> Bool。这样我们就得到了一个写法简单、表意清楚的表达式。
printIfTrue(predicate: 2>1)
printIfTrue(predicate: 2<1)

8、解决循环引用

方案一:weak

  • 通过weak修饰self对当前类进行弱引用

  • self属于可能有值也可能没有值的情况,因此weakSelf是一个可选类型

  • 又因为当前self一定存在,否则根本都不在这个方法里,所以在调用的时候可以使用强制解包(!),

weak var weakSelf = self

someView.block = {
    //执行当前类的某个方法
    weakSelf!.refreshView()
}

方案二:也是weak,属于方案一的简写

someView.block = { [weak self] in
    //执行当前类的某个方法
    self?.refreshView()
}

方案三:unowned

  • unowned有点类似oc中的unsafe_unretained

  • 它表示:即使它原本引用的对象被释放了,扔会保持着对这个已经被释放的对象有一个无效的引用,因此它不能是可选值,也不会指为nil

someView.block = { [unowned self] in
    //执行当前类的某个方法
    self.refreshView()
}