前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Swift3.0 - 函数和闭包

Swift3.0 - 函数和闭包

作者头像
酷走天涯
发布2018-09-14 14:47:33
1.1K0
发布2018-09-14 14:47:33
举报
文章被收录于专栏:每日一篇技术文章
函数的几种类型
  • 无参无返
代码语言:javascript
复制
func greet() -> Void {
}
// 或者
func greet(){    
}
  • 有参无返
代码语言:javascript
复制
func greet(person: String, day: String) {
    return "Hello \\\\(person), today is \\\\(day)."
}
greet(person: "Bob", day: "Tuesday")

思考1: 如何省略外部参数名?

代码语言:javascript
复制
  greet("John", "Wednesday")
 // 实现代码
  func greet(_ person: String, _ day: String) -> String {
    return "Hello \\\\(person), today is \\\\(day)."
}
  • 有参有返
代码语言:javascript
复制
func greet(_ person: String, on day: String) {
    return "Hello \\\\(person), today is \\\\(day)."
}
  • 无参有返
代码语言:javascript
复制
  func greet(_ person: String, on day: String) -> String {
    return "Hello \\\\(person), today is \\\\(day)."
}

中级思考
  • 参数和返回值

1.参数可以是那些?

基本类型的值,对象,数组,字典,元组,可变数量的参数,函数,闭包函数,协议,结构体,枚举值

2.怎么定义参数

a. 单值

代码语言:javascript
复制
 func calculate(a:Int){
    let b = a
}

b.多值

代码语言:javascript
复制
func calculate(a:Int...){
    for _ in a{
     }
}
// 调用
calculate(a: 1,2,3,4,5,6)

c.元组

代码语言:javascript
复制
func calculate(a:(name:String,age:Int)){
    let name = a.name;
    let age = a.age;
}

d.数组

代码语言:javascript
复制
 func calculate(a:[String]){
    for student in a {
}
}

e.定义字典

代码语言:javascript
复制
 func calculate(a:[String:Int]){
    for student in a {
        print(student.key)
        print(student.value)
  }
}

f.函数作为参数

代码语言:javascript
复制
 func add(a:Int,b:Int)->Int{// 作为函数参数的函数
  return a+b
}

func calculate(a:(Int,Int)->Int){// 定义的参数为函数的函数
    a(2,1)// 执行函数
}
calculate(a: add);// 执行函数

g.上面函数的闭包写法

代码语言:javascript
复制
  calculate { (a,b) -> Int in
    return a+b
}
calculate { (a,b) in a+b } // 省略写法(由于swift有推断能力,这样写它就能帮你推断出来上面的写法)

h. 参数为协议的方法

代码语言:javascript
复制
 protocol Player{  // 定义协议
      func play()
}

func playMusicWithPlayer(player:Player){
    player.play()
}

i.参数为结构体

代码语言:javascript
复制
struct Student{
    var name:String
    var age:Int
};

func getStudentDescription(student:Student){
    print(student.name)
    print(student.age)
}

j.参数为枚举类型

代码语言:javascript
复制
// 定义枚举值
enum CarType:String{
  case Lincoln = "林肯"
  case MERCURY = "水星"
  case SUZUKI = "铃木"
}
// 参数为协议的方法
func describeCar(carType:CarType){
    print(carType.rawValue);
}
  • 函数的内部定义函数

需求: 创建一个接口,输入true 返回 两个数相加的函数,输入false 返回两个数相减的函数

代码语言:javascript
复制
func generateFuncByFlag(flag:Bool)->(Int,Int)->Int{
// 定义两数字相加函数
func add(a:Int,b:Int)->Int{
    return a+b;
}
// 定义两数字相减函数
func decrease(a:Int,b:Int)->Int{
    return a-b;
}
// 根据输入的条件返回对应的函数
if flag{
    return add
}else{
    return decrease
}
}
// 生成对应的函数
let addFunc = generateFuncByFlag(flag: false)
// 执行返回的函数
print(addFunc(1,2))
  • 设置默认参数值
代码语言:javascript
复制
func addStudent(student:(name:String,score:Double)=("姓名",12)){
    print(student.name)
    print(student.1)
}
addStudent()
addStudent(student: ("酷走天涯",99))

提示:

元组类型,不能分别给参数赋值,比如像下面这样

代码语言:javascript
复制
// 这样是错误的方式
func addStudent(student:(name:String = "酷走天涯",score:Double = 12 )){
print(student.name)
print(student.1)
}
  • inout的使用

需求: 创建一个函数,交换两个Int类型值

a.如果参数为let修饰的常量

代码语言:javascript
复制
func swapTwoInts( a:  Int, b: Int){
let temporaryA = a
a = b
b = temporaryA
}

提示:

报错:系统提示错误,说常量不能修改值

b.我们将参数变成变量var

代码语言:javascript
复制
func swapTwoInts( var a:  Int, var b: Int){
let temporaryA = a
a = b
b = temporaryA
}

提示:

报错,不能使用var 修饰参数

c.inout 修饰的参数可以修改值

代码语言:javascript
复制
func swapTwoInts(  a: inout Int, b:inout Int){
    let temporaryA = a
    a = b
    b = temporaryA
}
var a = 30
var b = 40
swapTwoInts(a: &a, b: &b)
print(a)
print(b)

运行结果:

40 30

你需要注意的

1.inout的位置 在: 后面,数据类型前面 2.inout 修饰的参数不能有默认值 3.inout 不能用于修饰多值(如a:Int...)

  • 定义函数类型的变量
代码语言:javascript
复制
  func swapTwoInts(  a: inout Int , b:inout Int){
    let temporaryA = a
    a = b
    b = temporaryA
  }

  var swap1:( inout Int, inout Int)->Void = swapTwoInts

注意:函数类型的变量不能用标签修饰参数

代码语言:javascript
复制
// 错误的写法  不能使用a,b标签
var swap1:( a :inout Int, b: inout Int)->Void = swapTwoInts
// 你应该像下面这样
var swap1:( _ :inout Int, _: inout Int)->Void = swapTwoInts
// 或者下面这样也可以,a,b 不一定要和实际函数对应
var swap1:( _ a:inout Int, _ b: inout Int)->Void = swapTwoInts
// 建议还是用下面这种
var swap1:( inout Int, inout Int)->Void = swapTwoInts
  • 定义闭包类型数据
代码语言:javascript
复制
var customersInLine = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
let customerProvider = { customersInLine.remove(at: 0)}
print(customersInLine.count)
print("Now serving \\\\(customerProvider())!")
print(customersInLine.count)

运行结果:

5 Now serving Chris! 4 提示:上面那种闭包其实是五参有返的闭包形式,原形如下

代码语言:javascript
复制
let customerProvider:()->String= { customersInLine.remove(at: 0)}
  • 关键字 @discardableResult

先看一段代码:

代码语言:javascript
复制
class OSStudent{
var name:String!
var score:Double!
func setNewScore(score:Double)->Bool{
    if name == nil || name.isEmpty{
    return false
    }
    self.score = score
    return true
}
}
OSStudent().setNewScore(score: 34.0)

注意:

函数的setNewScore 方法有返回值,但是调用的时候,没有使用常量或者变量接受这个返回值,系统会产生警告如下图

让学习成为一种习惯

我们通过加关键字@discardableResult去除那种警告

代码语言:javascript
复制
@discardableResult
func setNewScore(score:Double)->Bool{
    if name == nil || name.isEmpty{
    return false
    }
    self.score = score
    return true
}

注意

如果你没有添加这个关键字,系统默认添加的是 @warn_unused_result ,有返回值没有使用会发生警告


高级思考
  • 如何获取,函数自己的名称,在那个文件中,在文件多少行
代码语言:javascript
复制
// 定义一个获取获取函数名称,获取文件路径的函数
func getFunctionName(name:String = #function,line:Int =   #line,file:String = #file){
print(name)
print(line)
print(file)
}
// 比如我们要获取下面函数的信息,只需要将函数写入要获取信息函数的内部调用即可
func  getUserName(){
 getFunctionName()
 }
 // 执行函数
getUserName()

运行结果:

getUserName() 152 /var/folders/gk/zc__29js08g1g03xrzgl8m1m0000gn/T/./lldb/2184/playground65.swift

  • 编译器可能没有那么智能
代码语言:javascript
复制
// 定义一个父类
class Person{
}
// 定义一个男人
class Man:Person{
}
// 定义一个女人
class Woman:Person{
}

// 定义三个描述人的方法
func describePerson(_ person:Person){
    print("我是人类")
}

func describePerson(_ woman:Woman){
    print("我是女人")
}

func describePerson(_ person:Man){
    print("我是男人")
}

 // 定义一个描述男人的女人的方法
func descripePerson(_ person:Person,_ woman:Woman){
  describePerson(person)
  describePerson(woman)
}
// 执行
descripePerson(Man(), Woman())

结果:

我是人类 我是女人

分析:

参数man 在值没有传入之前,被默认为Person 进行编译了,所以不管我们传入男人或者女人都之调用人类描述的方法。

那么我们应该怎么处理这个问题呢?

代码语言:javascript
复制
func descripePerson(_ person:Person,_ woman:Woman){
if person is Woman{
    describePerson(person as! Woman)
}else{
    describePerson(person as! Man)
}
describePerson(woman)
}

运行结果:

我是男人 我是女人

下面这种写法也是可以的

代码语言:javascript
复制
func descripePerson(_ person:Person,_ woman:Woman){
if let woman = person as?  Woman{
    describePerson(woman)
}else{
    describePerson(person as! Man)
}
describePerson(woman)
}
  • 泛型

需求: 设计一个接口,交换两个元素(数字,字符,对象)的值

代码语言:javascript
复制
  func swap<T>(a:inout T,b:inout T){
    (a,b) = (b,a)
 }

测试1

代码语言:javascript
复制
var a = "你好"
var b = "酷走天涯"
print("交换前---------------------")
print(a)
print(b)
swap(&a, &b)
print("交换后----------------------")
print(a)
print(b)

运行结果:

交换前--------------------- 你好 酷走天涯 交换后---------------------- 酷走天涯 你好

测试2

代码语言:javascript
复制
class Woman{
    var name = "女人"
    init(name:String) {
        self.name = name
    }
}
print("交换前---------------------")
print(a.name)
print(b.name)
swap(&a, &b)
print("交换后----------------------")
print(a.name)
print(b.name)

运行:

交换前--------------------- 小红 小白 交换后---------------------- 小白 小红

提示

交换的必须是相同的对象

*@escaping 用法

代码语言:javascript
复制
var downloadComplate:(Bool)->()
func downloadResource(url:String,complate:(Bool)->()){
  downloadComplate = complate
  // 异步下载,下载完成调动
  downloadComplate(true)
  // 下载失败
  downloadComplate(false)
}

运行

编译报错,提示没有加@escaping

@escaping 作用

我们经常在下载等异步操作完成时,才调用闭包函数,我们有可能暂时不要把这个闭包存放在数组中,或者使用属性去引用它,那么这个时候就需要使用这个关键了

修改代码

代码语言:javascript
复制
var downloadComplate:((Bool)->())
func downloadResource(url:String,complate:@escaping (Bool)->())  {
  downloadComplate = complate
 // 异步下载,下载完成调动
  downloadComplate(true)
  // 下载失败
  downloadComplate(false)
}

报错提示:

downloadComplate 使用之前必须初始化

所以进行初始化

代码语言:javascript
复制
var downloadComplate:((Bool)->())! // 加? 也可以,但是在调用时,要进行解包
  func downloadResource(url:String,complate:@escaping (Bool)->())  {
  downloadComplate = complate
 // 异步下载,下载完成调动
  downloadComplate(true)
  // 下载失败
  downloadComplate(false)
}

我们如何调用

代码语言:javascript
复制
downloadResource(url: "www.baidu.com") { (flag) in
print(flag)
}

如果我们不需要引用完全可以不使用关键字@escaping

代码语言:javascript
复制
func someFunctionWithNonescapingClosure(closure: () -> Void) {
    closure()
}
  • 关键字@autoclosure 的用法

a.不加自动闭包的关键字@autoclosure

代码语言:javascript
复制
func serve(customer customerProvider: () -> String) {
    print(customerProvider())
}
serve { () -> String in
    return "没加@autoclosure"
}

运行结果:

没加@autoclosure

b.添加@autoclouse

代码语言:javascript
复制
func serve(customer customerProvider: @autoclosure () -> String)     {
    print (customerProvider())
}
serve(customer: "加了@autoclosure") // 调用

是不是感觉参数像是字符串,而是下面这样,系统帮你自动闭包了

代码语言:javascript
复制
serve(customer: { "加了@autoclosure"})

如果还不清楚,其实是参数是一个返回值

代码语言:javascript
复制
serve(customer: { return "加了@autoclosure"})

完整的写法其实是下面这样

代码语言:javascript
复制
serve(customer: { () in return "加了@autoclosure"})

c. @autoclosure 和 @escaping 组合使用方法

代码语言:javascript
复制
func serve(customer customerProvider: @autoclosure @escaping() -> String) {
customerProvider1 = customerProvider
print (customerProvider())
}
serve(customer:  customersInLine.remove(at: 0))

提示:

其实自动闭包给人可能造成一种表意不清的感觉,建议使用的时候,一定要注释说明,或者不要使用。

d. @noescape

代码语言:javascript
复制
func calculate(fun :@noescape ()->()){
}

提示:

1.系统默认为@onescape 的类型 2.不能被引用 3.不能在异步执行

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2016.10.04 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 函数的几种类型
  • 中级思考
  • 高级思考
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档