窥探Swift之函数与闭包的应用实例

简介:

 今天的博客算是比较基础的,还是那句话,基础这东西在什么时候都是最重要的。说到函数,只要是写过程序就肯定知道函数是怎么回事,今天就来讨论一下Swift中的函数的特性以及Swift中的闭包。今天的一些小实例中回类比一下Objective-C中的函数的写法等等。Swift中的函数还是有许多好用的特性的,比如输入参数,使用元组返回多个值, 定义形参名,设定默认参数以及可变参数等等一些好用的特性。而在Swift中的闭包就是Objective-C中的Block, 除了语法不通外,两者的用法是一样的。废话少说,开始今天的主题,先搞一搞Swift中的函数,然后在搞一搞Swift中的闭包。

  一.Swift中的函数

    1. 函数的定义与使用

     在介绍Swift中的函数之前,我想用Objective-C中的一个简单的加法函数来作为引子,然后类比着实现一下Swift中相同功能的函数。关于函数定义就比较简单了,就是一些语法的东西,下面的代码片段是Objc中求两个整数之和的函数,并返回两个数的和。

- (NSInteger)sumNumber1:(NSInteger) number1
                number2:(NSInteger) number2 {
    return number1 + number2;
}
    函数的功能比较简单了,就是把两个整数传进来,然后返回两个整数的和。接下来将用Swift语言实现,也好通过这个实例来熟悉一下Swift语言中定义函数的语法。下方是Swift语言中求两个整数之和的函数。语法比较简单了,在Swift中定义函数,我们会使用到关键字 func 来声明函数。
1 //函数定义
2 func sum (number1:Int, number2:Int) -> Int{
3     return number1 + number2;
4 }

    用文字来描述Swift定义基本函数的语法就是:  func 函数名 (形参列表) -> 返回值类型 { 函数体},这样你就可以定义一个函数了。当然,函数定义时还有好多其他的用法,下面会详细介绍。上面函数的调用方法如下:
1 let sumTwoNubmer = sum(2, number2: 3);

 

    2. 函数中的形参列表

    关于函数中的形参列表还是有必要提上一嘴的,因为形参列表作为函数数据源之一,所以把参数列表好好的搞一搞还是很有必要的。参数列表也有很多好用的使用方式,接下来详细的介绍一下函数的形参列表。

    (1) 默认的形参是常量(let)

      在函数的形参列表中,默认的形参是常量。也就是相当于用let关键字对形参进行修饰了。我们可以做个试验,把上面加法函数做一个修改,在加法函数中对number1进行加1操作,你会得到一个错误,这个错误的大体意思就是“number1是不可被修改的,因为它是let类型的常量”。并且编译器还给人出了Fix-it(修复)的方案,就是在number1前面使用var关键字进行修饰,使其成为变量,这样才可以修改其值。

      上面说这么多,一句话:形参默认是常量,如果你想让其是变量,那么你可以使用var关键字进行修饰,这样被关键字var修饰的变量在函数中就可以被修改。下方就是报的这个错误,和编译器提供的解决方案。(在Objc中默认可以在函数中改变形参的值)

 

    (2)给形参命名

      为了代码的可读性和可维护性,我们在定义函数时,需要为每个参数名一个名字,这样调用者见名知意,很容易就知道这个参数代表什么意思了。接下来还是在上述加法函数中进行修改,为每个参数名一个名字,并看一下调用方式。修改上面的函数,给第一个形参命名成numberOne, 第二个形参为numberTwo, 下方是修改后的函数。 紧接着sum()函数的调用方式也会有所改变,在调用函数时编译器会给出参数的名称,这样调用者一目了然。

//函数定义
func sum (numberOne number1:Int, numberTwo number2:Int) -> Int{
    return number1 + number2;
}

let sumTwoNubmer = sum(numberOne: 10, numberTwo: 20);

      调用上述函数时,下方是编译器给出的提示,一目了然呢。

     关于Swift中参数名的内容,要说明的是在Swift1.0的时候,你可以在参数前面添加上#号,然后参数名就与变量(或者常量)的名字相同,而Swift2.0后这个东西去掉了,因为默认就相当于Swift1.0中添加#号。

 

    (3) 函数的传参与传引用

      先暂且这么说着,在C语言的函数中可以给函数传入参数,或者传入实参的内存地址就是所谓的传引用。如果传入的是引用的话,在函数中对值进行修改的话,那么出了函数,这个被修改的值是可以被保留的。在Swift中也是可以的,不过你需要使用inout关键字修饰形参,并且在使用该函数时,用&来修饰。这一点和C语言中类似,&就是取地址符。下方是inout使用的一个小实例。

1 func incrementStepTwo (inout myNumber:Int) {
2     myNumber += 2
3 }
4 var myTestNumber = 6
5 incrementStepTow(&myTestNumber)  //myTestNumber = 8

      myTestNumber变量经过incrementStepTwo()函数后,其值就会增加2。当然前提是myTestNumber是变量,如果myTestNumber是常量的话,那么对不起,调用该函数就会报错,下面是把var改成let后IDE给的错误提示。错误原因很显然是你动了一个不该动的值,也就是常量不可再次被修改的。

 

    

    (4) 不定参数函数

      不定参数函数也就是形参的个数是不定的,但是形参的类型必须是相同的。不定形参在使用时怎么取呢?不定个数的形参实际上是一个数组,我们可以通过for循环的形式来遍历出每个形参的值,然后使用就可以了。下方incrementMultableAdd()函数的形参的个数是不定的,其功能是求多个整数的和。在函数中我们只需遍历每个参数,然后把每个参数进行相加,最后返回所求的和即可。函数比较简单,再此就不在啰嗦了。

 

    (5) 默认形参值

      在Swift语言中是支持给形参赋初始值的,这一点在其他一些编程语言中也是支持的。但是Objective-C这么看似古老的语言中就不支持给形参指定初始值,在Swift这门现代编程语言中是支持这一特性的。默认参数要从参数列表后开始为参数指定默认值,不然就会报错。下方就是为函数的形参指定默认参数的示例。一个表白的方法sayLove(), 形参youName默认是“山伯”, 形参loverName默认是“英台”。 紧接着是sayLove函数的三种不同的调用方式,在调用函数时你可以不传参数,可以传一个参数,当然传两个也是没问题的。

 

      因为函数的每个参数都是有名字的,在含有默认参数的函数调用时,可以给任意一个参数进行传值,其他参数取默认值,这也是Swift的一大特色之一,具体请看如下简单的代码示例:

 

    3.函数类型

     每个函数都有自己的所属类型,函数类型说白了就是如果两个函数参数列表相同以及返回值类型相同,那么这两个函数就有着相同的函数类型。在Swift中可以定义一个变量或者常量来存储一个函数的类型。接下来将用过一个实例还介绍一下函数类型是个什么东西。

       (1) 首先创建两个函数类型相同的函数,一个函数返回两个整数的差值,另一个函数返回两个整数的乘积。当然这两个函数比较简单,直接上代码:

//现定义两个函数类型相同的函数
func diff (number1:Int, number2:Int) -> Int {
    return number1 - number2;
}

func mul (number1:Int, number2:Int) -> Int {
    return number1 * number2;
}

     (2) 函数定义好后,接着要定义个一个枚举来枚举每种函数的类型,下面定义这个枚举在选择函数时会用到,枚举定义如下:
1 //定义两种计算的枚举类型
2 enum CountType:Int {
3     case DiffCount = 0
4     case MulCount
5 }

     (3) 接下来就是把(1)和(2)中定义的东西通过一个函数来组合起来。说白了,就是定义个函数来通过枚举值返回这个枚举值所对应的函数类型。有时候说多了容易犯迷糊,就直接上代码得了。下方函数的功能就是根据传进来的枚举值来返回相应的函数类型。
//选择类型的函数,并返回相应的函数类型
func choiseCountType(countType:CountType) -> ((Int, Int) -> Int) {
    //函数类型变量
    var myFuncType:(Int, Int) -> Int
    
    switch countType {
    case .DiffCount:
        myFuncType = diff
    case .MulCount:
        myFuncType = mul
    }
    return myFuncType;
}

     (4) 接下来就是使用(3)中定义的函数了,首先我们需要定义一个相应函数类型((Int, Int) -> Int)的变量来接收choiseCountType()函数中返回的函数类型,然后调用该函数类型变量,在Playground中执行的结果如下:

 

      4.函数嵌套

    我们可以把 3 中的代码使用函数嵌套进行重写,在Swift中是支持函数嵌套的。 所以可以吧3.1和3.2中的函数放到3.3函数中的,所以我们可以对上述代码使用函数嵌套进行重写。使用函数嵌套重写后的代码如下所示,当然,choiseCountType()函数的调用方式没用发生改变,重写后的调用方式和3.4中的调用方式是一样一样的。

//选择类型的函数,并返回相应的函数类型
func choiseCountType(countType:CountType) -> ((Int, Int) -> Int) {
    
    //现定义两个函数类型相同的函数
    func diff (number1:Int, number2:Int) -> Int {
        return number1 - number2;
    }
    
    func mul (number1:Int, number2:Int) -> Int {
        return number1 * number2;
    }

    
    //函数类型变量
    var myFuncType:(Int, Int) -> Int
    
    switch countType {
    case .DiffCount:
        myFuncType = diff
    case .MulCount:
        myFuncType = mul
    }
    return myFuncType;
}

 

  二. 闭包

    说道Swift中的闭包呢,不得不提的就是Objective-C中的Block, 其实两者是一个东西,使用方式以及使用场景都是相同的。我们完全可以类比着Objective-C中的Block来介绍一下Swift中的Closure(闭包)。其实就是匿名函数。接下来的这段内容,先介绍一下Swift中Closure的基本语法,然后在类比着ObjC中的Block窥探一下Closure的使用场景。

    1.Closure变量的声明

      Closure就是匿名函数,我们可以定义一个闭包变量,而这个闭包变量的类型就是我们上面介绍的“函数类型”。定义一个闭包变量其实就是定义一个特定函数类型的变量,方式如下。因为Closure变量没有赋初始值,所以我们把其声明为可选类型的变量。在使用时,用!强制打开即可。

1 var myCloure0:((Int, Int) -> Int)?

      除了上面的方式外,我们还用另一种常用的声明闭包变量的方式。那就是使用关键字typealias定义一个特定函数类型,我们就可以拿着这个类型去声明一个Closure变量了,如下所示
1 //定义闭包类型 (就是一个函数类型)
2 typealias MyClosureType = (Int, Int) -> Int
3 var myCloure:MyClosureType?

 

    2. 给Closure变量赋值

      给Closure变量赋值,其实就是把一个函数体赋值给一个函数类型的变量,和函数的定义区别不大。但是给闭包变量赋值的函数体中含有参数列表,并且参数列表和真正的函数体之间使用关键字in来分割。 闭包可选变量的调用方式与普通函数没什么两样,唯一不同的是这个函数需要用!来强制打开才可以使用。赋值和调用方式如下。

 

    3. 闭包回调的应用实例

      暂且先称作闭包回调吧,其实就是Objc中的Block回调。在Swift中的闭包回调和Objc中的Block回调用法一致,下方将会通过一个实例来介绍一下闭包的应用之一。下方会创建两个视图控制器,我们暂且称为FirstViewController和SecondViewController。在FirstViewController上有一个Label和一个Button, 这个Button用来跳转到SecondViewController, 而这个Label用来显示从SecondViewController中回调过来的值。 而SecondViewController也有一个TextField和一个Button, 点击Button就会把输入框中的值通过闭包回调回传到FirstViewController然后在FirstViewController上的Label显示。

      (1) 构建这个实例的第一步要做的就是使用Stroyboard把我们所需的控件布局好,并且管理相应的类。当然我们这个Demo的重点不在于如何去布局控件,如何去关联控件,以及如何去使用控件,所以上述的这些就不做赘述了。这个实例的重点在于如何使用Closure实现值的回调。下方是我们的控件布局和目录结构的截图,从Storyboard上的控件来看,功能也就一目了然了。点击“FirstViewController” 上的“Go SecondViewController”按钮,就会跳转到 “SecondViewController” 。 在SecondViewController视图上的输入框输入数值,点击Back按钮返回到FirstViewController, 同时把输入框中的文本通过闭包回调的形式回传过来在FristViewController的label上显示。大致就这个简单的功能。

 

      (2)FirstViewController.swift中的内容

        FirstViewController.swift中的内容比较简单,就关联一个Label控件和一个按钮点击的事件,点击按钮就会跳转到SecondViewController,具体代码如下,在此就不啰嗦了,请看代码中的注释。下方代码重要的一点是在跳转到SecondViewController时要实现其提供的闭包回调,以便接受回传过来的值。

//
//  FirstViewController.swift
//  SwiftDemo
//
//  Created by Mr.LuDashi on 15/11/18.
//  Copyright © 2015年 ZeluLi. All rights reserved.
//

import UIKit

class FirstViewController: UIViewController {

    @IBOutlet var showTextLabel: UILabel!       //展示回调过来的文字信息
    
    override func viewDidLoad() {
        super.viewDidLoad()
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }
    
    //点击按钮跳转到SecondViewController
    @IBAction func tapGoSecondViewControllerButton(sender: UIButton) {
        //从Storyboard上加载SecondViewController
        let secondVC = UIStoryboard(name: "Main", bundle: NSBundle.mainBundle()).instantiateViewControllerWithIdentifier("SecondViewController")as! SecondViewController
        
        //实现回调,接收回调过来的值
        secondVC.setBackMyClosure { (inputText:String) -> Void in
            self.showTextLabel.text = inputText
        }
        
        //push到SecondViewController
        self.navigationController?.pushViewController(secondVC, animated: true)
    }
}

 

      (3) SecondViewController.swift中的内容

      SecondViewController.swift中的内容也不麻烦,就是除了关联控件和事件外,还定义了一个闭包类型(函数类型),然后使用这个特定的函数类型声明了一个此函数类型对应的变量。我们可以通过这个变量来接受上个页面传过来的闭包体,从而把用户输入的值,通过这个闭包体回传到上个页面。具体代码实现如下:

//
//  SecondViewController.swift
//  SwiftDemo
//
//  Created by Mr.LuDashi on 15/11/18.
//  Copyright © 2015年 ZeluLi. All rights reserved.
//

import UIKit

typealias InputClosureType = (String) -> Void   //定义闭包类型(特定的函数类型函数类型)

class SecondViewController: UIViewController {
    
    @IBOutlet var inputTextField: UITextField!  //输入框,让用户输入值,然后通过闭包回调到上一个页面
    
    var backClosure:InputClosureType?           //接收上个页面穿过来的闭包块
    
    override func viewDidLoad() {
        super.viewDidLoad()
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }
    
    //闭包变量的Seter方法
    func setBackMyClosure(tempClosure:InputClosureType) {
        self.backClosure = tempClosure
    }
    
    @IBAction func tapBackButton(sender: UIButton) {
        if self.backClosure != nil {
            let tempString:String? = self.inputTextField.text
            if tempString != nil {
                self.backClosure!(tempString!)
            }
        }
        self.navigationController!.popViewControllerAnimated(true)
    }
}

 

    (4) 经过上面的步骤这个实例已经完成,接下来就是看一下运行效果的时间了。本来想做成Git动态图的,感觉实例功能简单,而且UI上也比较简单,就没做,还是看截图吧。运行效果的截图如下:

  4.数组中常用的闭包函数

    在Swift的数组中自带了一些比较好用的闭包函数,例如Map, Filter, Reduce。接下来就好好的看一下这些闭包,用起来还是比较爽的。

    (1) Map(映射)

      说到Map的用法和功能,不能不说的是如果你使用过ReactiveCocoa框架,那么对里边的Sequence中的Map的使用方式并不陌生。其实两者的使用方法和功能是极为相似的。如果你没使用过RAC中的Map,那也无关紧要,接下来我们先上段代码开看一下数组中的Map闭包函数。

      通过上面的代码段以及运行结果,我们不难看出,map闭包函数的功能就是对数组中的每一项进行遍历,然后通过映射规则对数组中的每一项进行处理,最终的返回结果是处理后的数组(以一个新的数组形式出现)。当然,原来数组中的元素值是保持不变的,这就是map闭包函数的用法与功能。 

 

    (2) Filter (过滤器)

      Filter的用法还是比较好理解的,Filter就是一个漏勺,就是用来过滤符合条件的数据的。在ReactiveCocoa中的Sequence也是有Filter的,用法还是来过滤Sequence中的数据的。而在数组中的Filter用来过滤数组中的数据,并且返回新的数组,新的数组中存放的就是符合条件的数据。Filter的用法如下实例,下方的实例就是一个身高的过滤,过滤掉身高小于173的人,返回大于等于173的身高数据。

    (3)Reduce 

      在ReactiveCocoa中也是有Reduce这个概念的,ReactiveCocoa中使用Reduce来合并消减信号量。在swift的数组中使用Reduce闭包函数来合并items, 并且合并后的Value。下方的实例是一个Salary的数组,其中存放的是每个月的薪水。我们要使用Reduce闭包函数来计算总的薪水。下方是DEMO的截图:

相关文章
|
5天前
|
存储 Swift iOS开发
使用Swift开发一个简单的iOS应用的详细步骤。
使用Swift开发iOS应用的步骤包括:创建Xcode项目,设计界面(Storyboard或代码),定义数据模型,实现业务逻辑,连接界面和逻辑,处理数据存储(如Core Data),添加网络请求(必要时),调试与测试,根据测试结果优化改进,最后提交至App Store或其它平台发布。
39 0
|
5天前
|
机器学习/深度学习 数据采集 TensorFlow
【Swift开发专栏】Swift与机器学习:构建智能应用
【4月更文挑战第30天】本文探讨了使用Swift开发机器学习应用,分为三个部分:机器学习基础(定义、类型及应用),Swift在机器学习中的作用(Swift for TensorFlow、Core ML及性能优势),以及实践技巧(数据预处理、特征工程、模型训练与部署、性能优化和用户界面集成)。通过学习,开发者能掌握构建智能应用的技能,利用Swift的性能和安全性提升应用效率。随着深入学习,开发者可探索更多高级特性和技术,如深度学习和复杂数据分析。
|
5天前
|
存储 API Swift
【Swift开发专栏】Swift函数与闭包的实战应用
【4月更文挑战第30天】本文介绍了 Swift 中函数和闭包的实战应用。首先,函数的基本使用包括定义、参数与返回值、函数类型以及高级技巧如嵌套函数。接着,讨论了闭包的语法,包括无名函数、作为函数参数、简写形式和尾随闭包。最后,展示了函数和闭包在实战中的应用,如排序过滤集合和处理异步任务的回调。
|
5天前
|
缓存 Swift UED
【Swift开发专栏】Swift应用的启动优化
【4月更文挑战第30天】本文介绍了Swift应用启动优化技巧,包括优化代码结构和资源加载。建议减少启动时加载的代码,采用延迟加载、分模块加载和懒加载策略;优化初始化代码,注意顺序、异步初始化和避免全局初始化。对于资源,压缩图片、使用矢量图和图片缓存可提升加载速度。文中还提供了实战案例,展示如何在代码中实施这些优化措施。
|
5天前
|
安全 Swift iOS开发
【Swift 开发专栏】Swift 与 UIKit:构建 iOS 应用界面
【4月更文挑战第30天】本文探讨了Swift和UIKit在构建iOS应用界面的关键技术和实践方法。Swift的简洁语法、类型安全和高效编程模型,加上与UIKit的紧密集成,使开发者能便捷地创建用户界面。UIKit提供视图、控制器、布局、动画和事件处理等功能,支持灵活的界面设计。实践中,遵循设计原则,合理组织视图层次,运用布局和动画,以及实现响应式设计,能提升界面质量和用户体验。文章通过登录、列表和详情界面的实际案例展示了Swift与UIKit的结合应用。
|
5天前
|
缓存 算法 Swift
【Swift 开发专栏】Swift 应用的性能优化技巧
【4月更文挑战第30天】本文探讨了Swift应用性能优化,强调理解性能瓶颈、针对性优化和平衡性能与代码质量的重要性。提出优化技巧,包括选择合适数据结构、避免不必要的对象创建、使用缓存、优化算法、减少计算、管理内存、利用多核处理、优化网络请求和界面渲染。通过实际案例分析证明了这些方法能有效提升应用性能和用户体验。开发者应持续关注新技术和方法,以适应不断提升的性能要求。
|
5天前
|
存储 安全 Swift
【Swift 开发专栏】使用 Swift 开发一个简单的 iOS 应用
【4月更文挑战第30天】本文介绍了使用 Swift 开发简单 iOS 待办事项应用的步骤。首先,阐述了 iOS 开发的吸引力及 Swift 语言的优势。接着,详细说明了应用的需求和设计,包括添加、查看和删除待办事项的功能。开发步骤包括创建项目、界面搭建、数据存储、功能实现,并提供了相关代码示例。最后,强调了实际开发中需注意的细节和优化,旨在帮助初学者掌握 Swift 和 iOS 开发基础。
|
5天前
|
存储 Swift
Swift 语言:什么是闭包(Closure)?它们与函数的区别是什么?
Swift 语言:什么是闭包(Closure)?它们与函数的区别是什么?
43 1
|
5天前
|
监控 Swift iOS开发
局域网计算机监控软件中利用Swift构建iOS端的移动监控应用
在局域网计算机监控软件的开发中,构建iOS端的移动监控应用是一项关键任务。本文将介绍如何利用Swift语言实现这一目标,通过多个代码示例展示关键功能的实现。
225 1
|
5天前
|
Swift
Swift中的函数
Swift中的函数
23 1

相关课程

更多