swift4.1 系统学习十 函数

swift的函数与其他语言中的函数还是有很多的不同点的, 我们在本节中一起学习swift中的函数都有哪些特性。

辅助文件, 在“统一的函数引用体系”中会用到。

//
//  test.h
//  swift10
//
//  Created by iOS on 2018/10/10.
//  Copyright © 2018年 weiman. All rights reserved.
//

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface test : NSObject

//g_block将是我们在是swift源文件中操作的Block对象
extern void (^ _Nullable g_block)(void);
//CTest函数用于查看回调效果。
extern void CTest(void);

@end

NS_ASSUME_NONNULL_END

//
//  test.m
//  swift10
//
//  Created by iOS on 2018/10/10.
//  Copyright © 2018年 weiman. All rights reserved.
//

#import "test.h"

@implementation test

void (^ _Nullable g_block)(void) = NULL;

void CTest(void) {
    if (g_block != NULL) {
        g_block();
    }
}

@end

本节主要内容:

// 函数
/*
学过任何一门语言的小伙伴们对函数都会感到不陌生。
Apple官方将swift中的函数定义为: 执行一个特定任务的,自包含的代码块。
*/

// 1. 函数的定义和调用
/*
func 函数名(参数1,参数2,...) -> 返回值 {
函数体
}
注意: 如果没有返回值, “->返回值” 可以省略
*/

//
//  main.swift
//  swift10
//
//  Created by iOS on 2018/9/29.
//  Copyright © 2018年 weiman. All rights reserved.
//

import Foundation

do {
    //无参数、无返回值的函数
    func hello() {
        print("你好")
    }
    
    //无参数,有返回值的函数
    func helloWorld() -> String {
        return "你好啊, 嘈杂的世界"
    }
    
    //有参数,无返回值的函数
    func hello(name: String) {
        print("你好, \(name)")
    }
    
    // 有参数,有返回值的函数
    func helloWorld(name: String) -> String {
        return "你好, \(name)"
    }
    
    //函数调用
    hello()
    hello(name: "太阳")
    let a = helloWorld()
    print(a)
    let b = helloWorld(name: "月亮")
    print(b)
}

do {
    func add(a1: [Int], a2: [Int]) -> [Int] {
        return a1 + a2
    }
    
    let a = add(a1: [1, 2, 3], a2: [4, 5, 6])
    print(a)
    
    // 求数组中的最大值和最小值
    func maxAndMin(a: [Int]) -> (max: Int, min: Int) {
        
        var max = a[0]
        var min = a[0]
        for i in a {
            if i > max { max = i }
            if i < min { min = i }
        }
        return (max, min)
    }
    
    let value = maxAndMin(a: [8,3,5,3,2,5,78,4,3,2,3,45,43,333,65,456,886,3433,33,44,56])
    print("max: \(value.max), min: \(value.min)")
}

// 2. 函数的实参标签
/*
每个函数否包含一个实参标签和一个形参名。
注意:

  1. 实参标签可以省略,但是形参名不能省略;
  2. 实参标签可以相同,形参名不能相同;
    */
do {
    func myFunc(firstName name: String, secondName name2: String) -> String {
        let str = name + "·" + name2
        return str
    }
    
    // 调用
    let name = myFunc(firstName: "迈克尔", secondName: "杰克逊")
    print(name)
}

// 3. 默认形参值
/*
swift编程语言可以对函数形参设置一个默认值。如果一个形参具有一个默认值,那么我们在调用
这个函数的时候可以无需对此参数传递实参,也就是说带有默认值的形参所对应的实参可以缺省。
*/

do {
    func hello(name: String = "wrold") {
        print("hello, \(name)")
    }
    
    //调用
    hello()
    hello(name: "🇨🇳")
    
    /*
     打印:
     hello, wrold
     hello, 🇨🇳
     */
    
    /*
     注意:
     swift的默认参数与C++相比更加的灵活,因为它不仅仅可以放在最后,
还可以放在参数列表的任何位置。
     */
    
    func myHello(name: String, years: String = "2018", ages: Int) {
        print("hello, \(name), 今年是\(years)年, 我已经编程\(ages)年了")
    }
    
    myHello(name: "杰克", ages: 10)
    myHello(name: "小土豆", years: "2020", ages: 5)
}

// 4. 不定个数的形参
/*
swift语言和C语言一样,支持不定个数的形参。
当我们使用不定个数的形参时,可以将它视作为它所指定类型的额数组,我们可以通过count属性获取实参所
传递过来的参数个数;通过下标操作符来获取对应的实参值。
*/

do {
    func foo(a: Int, b: Int...) {
        var sum = a
        if b.count > 0 {
            for i in b {
                sum += i
            }
        }
        print("sum = \(sum)")
    }
    
    foo(a: 0)
    foo(a: 0, b: 1)
    foo(a: 0, b: 1, 2, 3, 4, 5)
    
    func boo(a: Int..., b: Int = -1) {
        if a.count > 0 {
            print("value = \(a[0] + a[1])")
        } else {
            print("value = \(b)")
        }
    }
    
    boo()
    boo(a: 1, 2, 3)
    boo(a: 1, 2, 3, 4, b: 10)
    boo(b: 100)
}

// 5. 输入输出形参(inout)
/*
在swift语言中,所有的形参默认都是常量。因此我们在函数体内如果对一个普通的形参修改其值,那么
就会引发编译报错。
swift引入了一种输入输出形参,使得该形参的值不仅能被修改,而且还能影响它所对应的实参值。
//实现步骤

  1. 当函数在调用前,先将实参的值拷贝到所对应的输入输出形参中。
  2. 在执行函数中的代码时,对输入输出形参的值进行修改。
  3. 在函数返回之后,将修改后输入输出形参的值拷贝回对应的实参中。
    */
do {
    func foo(a: Int, b: inout Int) {
        b += a
    }
    
    var x = 100
    
    foo(a: 10, b: &x)
    print("x = \(x)")
    // 输出: x = 110 , 在函数内部,b = 110, 函数外部的参数x也跟着改变了,这就是 inout的作用
    
    var array = [1, 2, 3]

    foo(a: array[0], b: &array[2])
    print("array: \(array)")
    // 结果: array: [1, 2, 4]
    
    func boo(t: inout (Double, Double)) {
        t.0 *= 0.5
        t.1 *= 0.5
    }
    
    var point = (10.0, 20.0)
    boo(t: &point)
    print("point: \(point)")
}

// 6. 函数重载
/*
swift 语言跟C++ 一样,默认支持函数重载。
什么是函数重载?
如果在同一作用域以及名字空间中出现一组相同名字的函数,这些函数具有不同的形参类型或者形参个数,或是
带有不同的返回类型,那么称这组函数为重载函数。
*/

do {
    func foo() {
        print("无参数")
    }
    
    func foo(a: Int) {
        print("有一个整型参数 \(a)")
    }
    
    func foo(b: Float) {
        print("有一个浮点型参数 \(b)")
    }
    
    foo()
    foo(a: 10)
    foo(b: 20.0)
    
}

// 7. 函数类型和函数签名
/*
函数类型是由其形参列表中各个形参的类型与函数返回类型构成。
*/

do {
    // 返回值为 () -> void
    func foo() {
        print("这是一个函数")
    }
    
    // 返回值为 (Int) -> void
    func foo(_ a: Int) {
        print("有参数的函数 \(a)")
    }
    
    // 返回值为 (Int, String) -> String
    func foo(a: Int, b: String) -> String {
        return "\(a), \(b)"
    }
    
    let b = foo(_ :)
    b(100)
    
    let a = foo(a: b:)
    let r = a(10, "11")
    print(r)
    
    // 定义一个函数magic,含有一个输入输出参数 func,类型是 (Int) -> Void
    // magic的函数的类型为 (inout (Int) -> Void) -> Void
    func magic(fun: inout (Int) -> Void) {
        fun = foo(_ :)
    }
    
    var ref = foo(_ :)
    
    magic(fun: &ref)
    ref(200)
    
    let magicRef: (inout (Int) -> Void) -> Void = magic
    magicRef(&ref)
}

/*
 函数签名
 函数签名这个语法特性其实引申自OC的方法签名。在swift语言中,一个函数名并不是唯一标识当前函数的标志。
 而是需要通过结合每个形参所持有的实参标签。
 */
do {
    func foo() {
        print("foo")
    }
    
    func boo(_ : Void) {
        print("boo")
    }
    
    func moo(a: Int) {
        print("moo")
    }
    
    func poo(a: Int, _: Int, c: Int) {
        print("poo")
    }
    
    //调用
    foo()
    boo(())
    moo(a: 10)
    poo(a: 100, 20, c: 77)
    
}

do {
    func foo(_ : Void) {
        print("foo Void")
    }
    
    func foo(a: Int) {
        print("foo Int")
    }
    
    func foo(i: Float) {
        print("foo float")
    }
    
    func foo(j: Double) {
        print("foo Double")
    }
    
    func foo(_: Void, _: Int) {
        print("foo Void,Int")
    }
    
    let ref1 = foo(_:) as (()) -> Void
    ref1(())
    
    let ref2 = foo(a:)
    ref2(100)
    
    let ref3 = foo(i:)
    ref3(0.5)
    
    let ref4 = foo(_: _:)
    ref4((), 10)
}

print("\n")

//8.嵌套函数定义
/*
swift语言中,允许在函数中定义一个嵌套函数,这是很多其他语言多不具备的。
*/

do {
    func hello(a: Int) {
        print("value = \(a)")
    }
    
    func outside() -> (Int) -> Void {
        let a = 10
        var b = 20
        
        //定义一个嵌套函数
        func inner(input: Int) {
            print("inner value = \(input)")
        }
        
        func hello(input: Int) {
            b += a + input
            print("b = \(b)")
        }
        
        //调用内部函数
        inner(input: a)
        
        return hello
    }
    
    hello(a: 100)
    let ref = outside()
    ref(5)
    ref(5)
    
    /*
     输出结果:
     value = 100
     inner value = 10
     b = 35
     b = 50
     */
}

// swift的嵌套函数不仅可以直接定义在函数体内,还可以定义在语句块作用域内。
do {
    func foo() {
        print("外面的foo")
        
        func foo() {
            print("中层的foo")
            
            func foo() {
                print("内部的foo")
            }
            
            //调用
            foo()
        }
        foo()
    }
    
    foo()
}

/*
 打印结果:
 外面的foo
 中层的foo
 内部的foo
 
 个人建议:在没有必要的情况下,还是不要这么写,可能自己都给绕晕了。
 */

//9.统一的函数引用体系
/*
什么是统一的函数引用?
就是在swift中,我们可以定义一个引用对象,这个对象除了能够引用函数之外,还能引用闭包以及结构体、
枚举、类类型对象的实例方法。
*/

print("\n ------------统一的函数引用体系----------------- ")
do {
    func foo () {
        print("foo")
    }
    
    var ref = foo
    ref()
    // g_block是OC中定义的Block对象,现在指向foo函数
    g_block = foo
    // 调用OC中的CTest,判断g_block是否为空
    CTest()
    /*
     打印结果
     foo
     foo
     */
    // 说明g_block已经指向了foo函数
    
    // 定义一个boo函数,返回值是() -> Void
    func boo() -> () -> Void {
        var a = 10
        /*
         定义一个嵌套的函数inner,捕获了局部变量a,因此它是一个闭包
         */
        func inner() {
            a += 10
            print("a = \(a)")
        }
        
        return inner
    }
    
    ref = boo()
    ref()
    
    
    g_block = boo()
    CTest()
    /*
     打印结果:
     a = 20
     a = 20
     */
    
    //从打印结果看,g_block还是不为空,并且指向了inner函数
    
    // 定义了一个结构体类型
    struct MyStruct {
        
        let a: Int, b: Double
        
        func method () {
            print("a = \(a), b = \(b)")
        }
    }
    
    let obj = MyStruct.init(a: 10, b: 100.0)
    
    //ref指向了一个结构体的实例方法
    ref = obj.method
    ref()
    
    g_block = ref
    CTest()
    
    /*
     结果:
     a = 10, b = 100.0
     a = 10, b = 100.0
     */
    
    /*
     小结:
     我们发现,swift中的一个函数类型引用可以指向函数、闭包、实例方法三种类型中的任意一种,这是很方便也是很先进的。
     我们跟其他语言做个对比:
     C++:指向函数的指针类型与指向成员函数的指针类型是完全不一样的。
     OC:没有指向成员函数的指针,而是统一使用selector机制。
     Java:使用指向方法的引用更加的繁琐。
     
     比较遗憾的是,函数引用不能比较相等性,因为swift会对函数、闭包做相关优化,因此在运行时无法确定两个引用是否是完全相同的。
     */
}

// 把函数的引用放在数组、字典等容器中。
do {
    func foo () {
        print("这是一个函数")
    }
    
    let ref = foo
    
    let array = [foo, ref]
    
    // 调用第一个元素
    array[0]()
    // 调用第二个元素
    array[1]()
    
//    报错:Binary operator '==' cannot be applied to two '() -> ()' operands
//    let isequal = foo == ref
//    print("isequal = \(isequal)")
}


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