swift简单总结(十六)—— 函数

版本记录

版本号 时间
V1.0 2017.07.27

前言

我是swift2.0的时候开始接触的,记得那时候还不是很稳定,公司的项目也都是用oc做的,并不对swift很重视,我自己学了一段时间,到现在swift3.0+已经出来了,自己平时也不写,忘记的也差不多了,正好项目这段时间已经上线了,不是很忙,我就可以每天总结一点了,希望对自己对大家有所帮助。在总结的时候我会对比oc进行说明,有代码的我会给出相关比对代码。
1. swift简单总结(一)—— 数据简单值和类型转换
2. swift简单总结(二)—— 简单值和控制流
3. swift简单总结(三)—— 循环控制和函数
4. swift简单总结(四)—— 函数和类
5. swift简单总结(五)—— 枚举和结构体
6. swift简单总结(六)—— 协议扩展与泛型
7. swift简单总结(七)—— 数据类型
8. swift简单总结(八)—— 别名、布尔值与元组
9. swift简单总结(九)—— 可选值和断言
10. swift简单总结(十)—— 运算符
11. swift简单总结(十一)—— 字符串和字符
12. swift简单总结(十二)—— 集合类型之数组
13. swift简单总结(十三)—— 集合类型之字典
14. swift简单总结(十四)—— 控制流
15. swift简单总结(十五)—— 控制转移语句

函数 - Functions

函数是swift的重点,函数用来完成特定任务的独立的代码块,可以给一个函数起一个名字,当函数执行的时候,这个名字就会被“调用”。swift的函数风格很灵活,可以像C风格的参数,也可以像OC风格的参数,参数可以提供默认值,简化函数调用,参数也可以既当做传入参数,也可以当做传出参数,也可以说,一旦函数执行结束,传入的参数值可以被修改。

swift中,每个函数都有一个类型,包括函数的参数值类型和返回值类型,你可以把函数类型当做任何其他普通变量类型一样处理,这样可以简单的把函数当做别的函数的参数,也可以从其他函数中返回函数,函数的定义可以写在其他函数定义中,这样可以在嵌套函数范围内实现功能封装。

下面主要讲下面几个大问题:

  • 函数的定义与调用 (Defining and Calling Functions)
  • 函数参数与返回值 (Function Parameters and Return Values)
  • 函数参数名称 (Function Parameter Names)
  • 函数类型 (Function Types)
  • 函数嵌套 (Nested Functions)

函数定义与调用 - Defining and Calling Functions

定义一个函数时,可以定义一个或者多个有名称和类型的值,作为函数的输入,也可以定义一种类型的值作为函数执行结束的输出。每一个函数都有一个名字,用来描述执行的任务,可以通过函数的名字调用函数。

下面我们就看一个简单的例子。

class JJSwiftVC: UIViewController
{

    override func viewDidLoad()
    {
        super.viewDidLoad()
        
        view.backgroundColor = UIColor.lightGray
        
        print(greeting(personName: "Nana"))
    }
    
    func greeting(personName : String) -> String {
        return "Hello," + personName + "!"
    }
}

下面看输出结果

Hello,Nana!

函数参数与返回值 - Function Parameters and Return Values

swift可以有任意类型任意个数的参数,函数也可以作为参数传入,同时,swift可以有任意类型的返回值,函数也可以作为返回值,也可以没有返回值,总之,非常灵活。

1. 多重输入参数 - Multiple Input Parameters

函数可以有多个输入参数,写在圆括号中,用逗号分隔。

下面看一个简单的程序。

class JJSwiftVC: UIViewController
{
    override func viewDidLoad()
    {
        super.viewDidLoad()
        
        view.backgroundColor = UIColor.lightGray
        
        print(calculateDistance(start: 1, end: 100))
    }
    
    func calculateDistance(start : Int, end : Int)  -> Int {
        return end - start
    }
}

下面看一下输出结果

99

2. 无参函数 - Functions Without Parameters

函数可以没有参数,这时候的函数名称后面的圆括号里面就是空的。

class JJSwiftVC: UIViewController
{

    override func viewDidLoad()
    {
        super.viewDidLoad()
        
        view.backgroundColor = UIColor.lightGray
        
        print(greeting())
    }
    
    func greeting() -> String {
        return "Hello!"
    }
}

下面看输出结果

Hello!

3. 无返回值函数 - Functions Without Return Values

函数可以没有返回值,看下面的例子,相当于OC中返回类型为void的方法。

class JJSwiftVC: UIViewController
{

    override func viewDidLoad()
    {
        super.viewDidLoad()
        
        view.backgroundColor = UIColor.lightGray
        
        greeting(greet: "Nancy")
    }
    
    func greeting(greet : String) {
        print("Hello, " + greet)
    }

}

下面看输出结果

Hello, Nancy

注意:严格讲,虽然没有返回值被定义,greeting函数依然返回了值,没有定义返回类型的函数会返回特殊的值,叫Void,它其实是一个空的元组(tuple),没有任何元素,可以写成()。函数就算有返回值也可以忽略不用,但是有返回值的函数必须返回一个值。

4. 多重返回值函数 - Functions with Multiple Return Values

可以使用元组tuple类型让多个值作为一个复合值从函数中返回,下面看一个简单例子。

class JJSwiftVC: UIViewController
{

    override func viewDidLoad()
    {
        super.viewDidLoad()
        
        view.backgroundColor = UIColor.lightGray
        
        let number = count(string: "swift is very interesting !")
        print(number.vowels)
        print(number.consonants)
        print(number.others)
    }
    
    func count(string : String) ->(vowels : Int, consonants : Int, others : Int) {
        var vowels = 0, consonants = 0, others = 0
        for character in string.characters {
            switch String(character).lowercased() {
            case "a", "e", "i", "o", "u":
                vowels += 1
            case "b", "c", "d", "f", "g","h", "j", "k", "l", "m", "n", "p", "q", "r", "s", "t", "v", "w", "x", "y", "z":
                consonants += 1
            default:
                others += 1
            }
        }
    
        return(vowels, consonants, others)
    }
}

下面看输出结果

7
15
5

函数参数名称 - Function Parameter Names

函数的参数一般都这么定义:参数名(parameter name)这些参数只能在函数体内部使用,不能在调用函数时使用,这种类型的参数名称被称为局部参数名(local parameter)

1. 外部参数名 - External Parameter Names

有时候,调用函数时,给每个参数命名是非常有用的,因为这些参数名可以指出各个实参的用途,如果你希望函数的使用者在调用函数时提供参数名字,那就需要给每个参数除了局部参数名外再定义一个外部参数名,外部参数名写在局部参数名之前,用空格分开。还有几点要注意:

  • 如果你提供了外部参数名,那么函数被调用时,必须使用外部参数名。
  • 当其他人读你的代码,函数参数意图不是很明显时,考虑使用外部参数名,如果函数参数名的意图很清晰就不需要定义外部参数了。

下面看一个简单代码。

class JJSwiftVC: UIViewController
{

    override func viewDidLoad()
    {
        super.viewDidLoad()
        
        view.backgroundColor = UIColor.lightGray
        
        print(join(string: "Hello", toString: "world", withJoiner: "-"))
    }
    
    func join(string s1 : String, toString s2 : String, withJoiner joiner : String) -> String{
        return s1 + joiner + s2
    }
}

下面看输出结果

Hello-world

2. 简写外部参数名 - shorthand External Parameter Names

如果你需要提供外部参数名,但是局部参数名已经写好了,那么不用再修改了,你可以只写一次参数名,并用#作为前缀,就表明这个参数可以作为局部参数名和外部参数名。

注意:上面是swift 3.0以前的功能,3.0以后已经被去掉了,这里还加上这个选项,是让大家区别一下。

3. 默认参数值 - Default Parameter Values

你可以在函数体内为每个参数定义默认值,默认值被定义后,调用这个函数时可以忽略这个参数。下面看这个简单例子。

class JJSwiftVC: UIViewController
{

    override func viewDidLoad()
    {
        super.viewDidLoad()
        
        view.backgroundColor = UIColor.lightGray
        
        print(join(string: "Hello", toString: "World", withJoiner: "-"))
        print(join(string: "Hello", toString: "World"))
    }
    
    func join(string s1 : String, toString s2 : String, withJoiner joiner : String = "&&") -> String{
        return s1 + joiner + s2
    }
}

下面看输出结果

Hello-World
Hello&&World

可以发现:

  • 如果参数有默认值,调用如果传值就用我们传入的值,但是如果调用不传值就用参数的默认值。
  • 将带有默认值的参数放在函数参数列表的最后面,这样可以保证函数调用时候,非默认参数的顺序是一致的,同时可以清晰的看见相同的函数在不同情况下调用时显的更为清晰。

4. 默认值参数的外部参数名- External Names for Parameters with Default

当你没有给带默认值的参数提供外部参数名时,swift 会自动提供外部名字,函数调用的时候,外部参数名必须使用。下面看例子。

class JJSwiftVC: UIViewController
{

    override func viewDidLoad()
    {
        super.viewDidLoad()
        
        view.backgroundColor = UIColor.lightGray
        
        print(join(string: "Hello", toString: "World", withJoiner: " - "))
    }
    
    func join(string : String, toString : String, withJoiner : String = "&&") -> String{
        return string + withJoiner + toString
    }
}

下面看输出结果

Hello - World

注意:你也可以使用下划线_作为默认值参数的外部参数名,这样可以在调用时不用提供外部参数名,但是给带默认值参数命名总是更加合适的。

5. 可变参数 - Variadic Parameters

一个可变参数可以接受一个或多个值,函数调用时,你可以用可变参数来传入不确定数量的输入参数,通过在变量类型后面加上...的方式定义可变参数。

下面我们看一个例子。


class JJSwiftVC: UIViewController
{

    override func viewDidLoad()
    {
        super.viewDidLoad()
        
        view.backgroundColor = UIColor.lightGray
        
        print(sum(numbers: 1, 3, 4))
        print(sum(numbers: 100, 300, 500))
    }
    
    func sum(numbers : Double...) -> Double{
        var total = 0.0
        for number in numbers {
            total += number
        }
        return total
    }
}

下面看输出结果

8.0
900.0

还有几点要注意:

  • 传入函数的参数被当做这个类型的一个数组处理。
  • 一个函数最多只能有一个可变参数,而且它必须是参数表中最后的一个,这样做的目的是为了避免函数调用出现歧义。
  • 如果一个函数有一个或者多个带默认值的参数,而且还有一个可变参数,那么把可变参数放在参数表的最后。

6. 常量参数和变量参数 - Constant and Variable Parameter

函数参数默认是常量,在函数体内改变参数值会报错,但是有的时候有传入参数的变量值副本是很有用的,你可以通过指定一个或者多个参数为变量函数,从而避免自己在函数中定义新的变量,变量参数不是常量,你可以在函数体内部把它当做新的可修改副本来使用。

常量参数变为变量参数的方法就是在参数名前面加关键字var,说了这么多上面都是swift 3.0以前的写法,那么swift 3.0以后怎么写呢,看下面的例子。

class JJSwiftVC: UIViewController
{

    override func viewDidLoad()
    {
        super.viewDidLoad()
        
        view.backgroundColor = UIColor.lightGray
        
        var number = 10
        print(sum(number: &number, number2: 20))
    }
    
    func sum(number : inout Int, number2 : Int) -> Int {
        number += number2
        return number
    }
}

下面看输出结果

30

可以看见,现在不用var而是用inout了,这里需要注意的就是inout的位置。

7. 输入输出函数 - InOut Parameters

有的时候我们想要一个函数可以修改参数的值,并且当函数调用结束后,这个参数仍可用,这个时候我们就要把这个参数定义为输入输出参数- In - Out Parameters。定义输入输出参数,需要在参数定义前加一个inout关键字,一个输入输出参数有传入函数的值,这个值被函数修改,然后被传出函数,替换原来的值。

下面看一个简单的例子。

class JJSwiftVC: UIViewController
{

    override func viewDidLoad()
    {
        super.viewDidLoad()
        
        view.backgroundColor = UIColor.lightGray
        
        var number1 = 10
        var number2 = 20
        swap(&number1, &number2)
        print(number1)
        print(number2)

    }
    
    func swapNumbers(a : inout Int, b : inout Int){
        let temp = a
        a = b
        b = temp
    }
}

下面看输出结果

20
10

这里大家要注意inout的位置,放在类型说明之前,冒号之后。


函数类型

每一个函数都有函数类型,由函数参数类型和返回值类型组成。例如(Int, Int) -> Int它的意义就是:这个函数类型是,它有两个Int型的参数,并返回一个Int型的值。

下面我们看几种情况。

1. 使用函数类型 - Using Function Types

swift中,使用函数类型就像使用其他类型一样,例如你可以定义一个类型为函数的常量或者变量,并将函数赋值给它。

var mathFunction = (Int, Int) -> Int = addInts

上面这个读作:定义一个叫做mathFunction的变量,类型是一个有两个Int型的参数并返回一个Int型的值的函数,并将这个新变量指向addInts函数。

有了这个定义,以后你可以使用mathFunction调用被赋值的函数了,比如:

mathFunction(2, 3)

有相同类型的不同函数可以被赋值给同一个变量,就像非函数类型的变量一样,比如:

mathFunction = multiplyTwoInts

这里multiplyTwoIntsaddInts函数类型是相同的。

像其他数据类型一样,当赋值一个函数给常量或者变量的时,swift用来推断其函数类型。

let anotherMathFunction = addInts

这里anotherMathFunction类型也被推断为 (Int, Int) -> Int。

2. 函数类型作为参数类型 - Function Types as Parameter Types

这里函数类型作为参数类型并不传入函数是如何实现的,它只关心这个传入的函数类型是否正确,下面看一个简单的例子。

class JJSwiftVC: UIViewController
{

    override func viewDidLoad()
    {
        super.viewDidLoad()
        
        view.backgroundColor = UIColor.lightGray

        multiply(sum: sum, a: 1, b: 2)
    }
    
    func multiply(sum : (Int, Int) -> Int, a : Int, b : Int) {
    
        let result = sum(a, b) * a * b
        print("result = \(result)")
    }
    
    func sum(a : Int, b : Int) -> Int {
        return a + b
    }

}

下面看输出结果

result = 6

3. 函数作为返回类型 - Functions Type as Return Types

你可以用函数类型作为另一个函数的返回类型,你需要做的是在返回箭头->后写一个完整的函数类型。

可以看下面这个例子。

class JJSwiftVC: UIViewController
{
    
    var currentValue = 10;

    override func viewDidLoad()
    {
        super.viewDidLoad()
        
        view.backgroundColor = UIColor.lightGray
        
        while currentValue > 0 {
            let moveToZero = chooseStepFunction(input: currentValue > 0)
            var result = moveToZero(currentValue)
            print(result)
            currentValue -= 1
        }
    }
    
    func chooseStepFunction(input : Bool) -> (Int) -> Int {
        if input {
            return backForward
        }
        else {
            return stepForward
        }
    }
    
    
    func stepForward(input : Int) -> Int {
        return input + 1
    }
    
    func backForward(input : Int) -> Int {
        return input - 1
    }
}

下面看输出结果

9
8
7
6
5
4
3
2
1
0

嵌套函数 - Nested Functions

前面介绍的函数都是全局函数,你也可以把函数定义在别的函数体中,这种函数叫做嵌套函数,嵌套函数对于外界来说是不可见的,但是可以被它们封闭函数(enclosing Function)调用,一个封闭函数也可以返回它的某一个嵌套函数,使得这个函数可以在其他域中被使用。

我们接着看一个例子。

class JJSwiftVC: UIViewController
{
    
    var currentValue = 10;

    override func viewDidLoad()
    {
        super.viewDidLoad()
        
        view.backgroundColor = UIColor.lightGray
        
        while currentValue > 0 {
            let moveToZero = chooseStepFunction(input: currentValue > 0)
            var result = moveToZero(currentValue)
            print(result)
            currentValue -= 1
        }
    }
    
    func chooseStepFunction(input : Bool) -> (Int) -> Int {
        
        func stepForward(input : Int) -> Int {
            return input + 1
        }
        
        func backForward(input : Int) -> Int {
            return input - 1
        }
        
        if input {
            return backForward
        }
        else {
            return stepForward
        }
    }
}

下面看输出结果

9
8
7
6
5
4
3
2
1
0

后记

未完,待续~~~

小雪
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,290评论 4 363
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,399评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,021评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,034评论 0 207
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,412评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,651评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,902评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,605评论 0 199
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,339评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,586评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,076评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,400评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,060评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,083评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,851评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,685评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,595评论 2 270

推荐阅读更多精彩内容