Swift-函数、结构体、类、闭包

3,613 阅读8分钟

1.函数

1.1函数格式:

1).多个返回值时: func 函数名(参数名:参数类型, ...)-> (返回值类型, ...){}
2).单个返回值时: func 函数名(参数名:参数类型, ...)-> 返回值类型{}
3).无返回值时:func 函数名(参数名:参数类型, ...)-> ()func 函数名(参数名:参数类型, ...)-> Void(Swift标准库定义:public typealias Void = ())

Tips:
1).形参默认是let,也只能是let;
2).实现函数多个返回值,使用的其实是Swift中的元组(Tuple)类型

1.2 参数初始值

参数设置初始值参数名称:参数类型 = 初始值

func person(name:String ,age : Int,job : String = "free worker"){
    print("name = \(name),age = \(age),job = \(job)")
}

如上声明完函数后,在调用函数时可以看到:

截屏2021-12-24 下午2.32.09.png 由此可知:带有初始值的参数,在调用函数的时候可以给此参数传值,也可以不用给传值;

1.3 可变参数(Variadic Parameter)

格式为:参数名称:参数类型...

注意:
1).每个函数只能存在一个可变参数;
2).紧跟在可变参数后面的参数不能省略参数标签
3).可变参数的参数名称可以使用省略参数标签
4).省略参数标签和可变参数类型相同时,不要连续声明,不好区分入参给了哪个参数

func makeSentence(words : String...,other:String) -> String{
    var sentence = ""
    for word in words {
        sentence = sentence + " " + word
    }
    sentence = sentence + other
    return sentence
}
makeSentence(words: "Function","having", "Variadic parameters","Add as many strings here you want", "Can use one variadic parameter per func","Make full use",other:".")

1.4 参数标签(Argument Label)

可以修改参数标签;
1).参数标签格式为:func 函数名(参数标签 参数名:参数类型, ...)-> (返回列表){}

2).省略参数标签格式:func 函数名(_ 参数名:参数类型, ...)-> (返回列表){}
此时声明函数实例变量后,不显示参数名。

1.5 隐式返回(Implicit Return)

如果整个函数体是一个单一表达式,那么函数会隐式返回整个表达式,这时可以省略return关键字。

举个🌰:

func sum(v1 : Int, v2 : Int) -> Int{
    v1 + v2
}

此时sum函数的函数体是:v1 + v2;是一个单一表达式,可以省略return关键字。

1.6 函数重载(Function Overload)

函数重载规则:
1).函数名相同
2).参数个数不同 || 参数类型不同 || 参数标签不同
举个🌰:

    //原函数
    func sum(v1:Int,v2:Int)-> Int{
        v1 + v2
    }
    //参数标签不同
    func sum(_ v1 : Int,_ v2 : Int) -> Int{
        v1 + v2
    }
    func sum(a : Int,b : Int) -> Int{
        a + b
    }
    //参数个数不同
    func sum(v1:Int,v2:Int,v3:Int)-> Int{
        v1 + v2 + v3
    }
    //参数类型不同
    func sum(v1:Int,v2:Double) -> Double{
        Double(v1) + v2
    }
    func sum(v1:Double,v2:Int)-> Double{
        v1 + Double(v2)
    }

Tips:返回值类型与函数重载无关

func sum(v1:Int,v2:Int) -> Int{v1+v2}
func sum(v1:Int,v2:Int){}
sum(v1:10,v2:20)//报错:Ambiguous use of sum(v1:v2)

上面返回值类型不同,不构成函数重载。

1.6 函数使用

函数类型由:形式参数类型、返回值类型组成
函数类型可以作为参数、返回值;

1.6.1 函数定义为变量、常量

举个🌰:

//平方
func square(_ num : Int) -> Int{
    return num * num
}
square(4)
//立方
func cube(_ num : Int) -> Int{
    return num * num * num
}
cube(4)
//可以将函数,赋值给一个变量或常量,功能和作用与square相同
var exponentialFunction = square
exponentialFunction(4)
//打印结果:16
cube(exponentialFunction(4))
//打印结果:4092

1.6.2 函数作为入参

var integers = [1,2,3,4,5]
func sumOfExponentialsOf(array a: [Int], with function: (Int)->Int)->Int
{
    var result = 0
    for x in a
    {
        result = result + function(x)
    }
    return result
}
sumOfExponentialsOf(array: integers, with: exponentialFunction)
//打印结果:55

1.6.2 函数作为返回

返回值类型是函数的函数,称为高阶函数(Higher-Order Function)

1.6.3 嵌套函数(Nested Function)

将函数定义在函数内部,称为嵌套函数 举个🌰:定义一个函数,函数返回值类型为函数,内部具有嵌套函数

func chooseComputation(isSquared b : Bool) -> (Int)->Int{
    func square(_ num :Int)->Int//嵌套函数
    {
        return num*num
    }
    func cube(_ num :Int)->Int
    {
        return num*num*num
    }
    if b {
        return square
    }
    else{
        return cube
    }
}
var result = chooseComputation(isSquared: true)//result是(Int)->(Int)类型的函数。
result(2) //打印结果:4
result = chooseComputation(isSquared: false)
result(2) //打印结果:8

let value = chooseComputation(isSquared: true)(5)//此时value是Int类型,打印结果:25

2.结构体

在Swift中,绝大多数常见类型都是结构体,包括:Bool、Int、Double、String、Array、Dictionary等常见类型都是结构体。结构体有个几个明显特点:

特点1:所有结构体都有一个编译器自动生成的初始化器(initializer,初始化方法、构造器、构造方法).

struct Rectangle{
   var width
   var height
}
var rectangle = Rectangle.init(width: 10, height: 10)

特点2:编译器会根据情况,可能会为结构体生成多个初始化器,宗旨是:保证所有成员都有初始值。 举个🌰1:

struct Rectangle{
    var width : Int = 10
    var height : Int
}
//初始方法1:
var rectangle = Rectangle(width: 20, height: 20)

//初始方法2:
var rectangle2 = Rectangle(height: 10)

举个🌰2:此时width和height默认都是nil,相当于结构体成员变量都有值,所以不报错

struct Rectangle{
    var width : Int?
    var height : Int?
}
//初始方法1:
var rectangle = Rectangle(width: 20, height: 20)
//初始方法2:
var rectangle2 = Rectangle(height: 10)
//初始方法3:
var rectangle3 = Rectangle()

特点3:如果初始化的结构体为常量,即使结构体成员为变量,也不可更改


let rectangleConstant = Rectangle()
rectangleConstant.height = 20//报错
rectangleConstant.width = 20//报错

特点4:如果初始化的结构体为变量,结构体成员为常量,此时结构体成员不可更改

struct Circle{
    let radius : Int = 0
}
var circle = Circle()
circle.radius = 10//报错

特点4:一旦在定义结构体时自定义了初始化器,编译器就不会再为结构体自动生成其他初始化器。

3.类

1)、类结构:class 类名 {}
2)、类定义和结构体类似,但编译器并没有为类自动生成可以传入成员值的初始化器。
3)、如果类的所有成员都在定义的时候指定了初始值,编译器会为类生成无参的初始化器。
4)、类的内存中分别存储了:指向类型信息(8字节)+ 引用计数(8字节)+变量;类中的方法不存储在类的内存中。
5)、类内存对齐系数为16。

4.结构体和类的区别

1)结构体是值类型(枚举也是值类型),类为引用类型(指针类型);
2)编译会自动为结构体生成初始化方法;类不会,类手动声明
3)结构体内存不一定在栈空间内,取决于结构体变量定义的位置,如果在函数中定义结构体变量,结构体变量内存在栈空间中。
4)声明的类变量是一个指针变量,指针指向堆中存储的类。类一定存在堆空间中,类变量不一定,取决于定义类变量的位置。

5.值类型&引用类型

5.1值类型

1)值类型赋值:给var、let或者给函数传参,是直接将所有内容拷贝一份,属于深拷贝(deep copy)
2) 为了提高性能,String、Array、Dictionary、Set采取了Copy On Write的技术。仅当有“写”操作时,才会真正执行拷贝操作。
3)值类型包括:枚举(Optional)、结构体(Bool、Int、Float、Double、Character)、Array、Dictionary、Set。

5.2引用类型

  1. class是引用类型
  2. 引用赋值给var、let或者给函数传参,是将内存地址拷贝一份,属于浅拷贝。

6.闭包

函数也是闭包的一种;闭包是没有名字的函数,也没有func关键字.

6.1闭包表达式

闭包表达式:

{
 (参数列表) -> 返回值类型 in
 函数体代码
}

附加
1)当没有返回值时,或返回值类型可以从上下文推断出来,可以省略返回值类型部分;
2)当没有入参时,或入参类型可以从上下文推断出来时,可以省略参数列表部分的代码

6.2尾随闭包

尾随闭包是一个写在函数调用括号外面(后面)的闭包表达式。 mapsorted也是一种尾随闭包。

//将整数型数组转变为字符型数组
var numbersArray = [1,2,3,4,5,6]
var closureString = numbersArray.map{
    return "\($0)"
}
print(closureString)//打印结果:["1", "2", "3", "4", "5", "6"]

//使用尾随闭包按降序排列数组
var descendingArray = numbersArray.sorted{$0 > $1}
print(descendingArray)//打印结果:[6, 5, 4, 3, 2, 1]

6.3逃逸(转义)闭包

1)转义闭包:传入函数的闭包,在函数执行结束之后才会被调用的。换句话说,它比传递给它的函数更长寿。转义闭包通常用于完成处理程序,因为它们在函数结束后被调用。
2)非转义闭包:传入函数的闭包,在函数执行中会被调用的,即在它返回之前。默认情况下,闭包是不可转义的,需要使用关键@escaping修饰。

6.4自动闭包(@autoclosure)

自动闭包是一种自动创建的,用来把作为实际参数传递给函数的表达式打包的闭包。它不接受任何实际参数,并且当它被调用时,它会返回内部打包的表达式的值。
这个语法的好处在于通过写普通表达式代替显示闭包而使你省略函数形式参数的括号。
举个🌰:

//没有添加@autoclosure
func getFirstPositive(_ v1:Int,_ v2:()->Int) -> Int?{
    return v1 > 0 ? v1 : v2()
}
getFirstPositive(-4) {20}

//添加@autoclosure后
func getFirstPositive(_ v1:Int,_ v2:@autoclosure ()->Int) -> Int?{
    return v1 > 0 ? v1 : v2()
}
getFirstPositive(-4, 20)

附加:
@autoclosure只支持()-> T格式的参数;
空合并运算符??使用了@autoclosure技术;
有@autoclosure、无@autoclosure,构成了函数重载;