读书笔记-Swift-02函数

函数参数和返回值

  • 函数就是这么个玩意:加一些材料进去,通过一个过程,然后产出一些东西。放进去的就是input,输出来的就是output。input就是函数的参数,output就是函数的result。
fun sum (_ x: Int, _ y:Int) -> Int {
    let result = x + y
    return result
}
  • 除非在别的地方调用函数,传入特定类型的值,否则函数体的代码是不会运行的。没传,传错类型了,编译器都会报错。因此在函数体里,我们就很有信心的使用这个参数,知道它是有值的,并且类型也是指定的类型。

  • 参数的名字是本地变量。只在函数体内部有效。

  • 指定了返回值类型的函数,就必须要有返回值。并且类型也要匹配上。

  • return语句实际上干了两件事,其一是返回具体的值。其次是结束函数。因此return后面的语句是不执行的。

  • 函数头的声明实际上是以约定。函数调用的时候,也遵循这个约定。函数名作为调用。带着参数列表。重要的是类型要一致。返回值的类型也要一致。

  • 传入实参的名字无所谓,重要的是它的值。

  • 函数调用的时候,函数返回值可以不保存下来。当然,编译器会给个警告。如果你实在不需要这个返回结果,你可以赋值给一个匿名变量(_)。或者在前面加上 @discardableResult。

  • 函数返回值就是函数的值,可以直接当做返回类型使用。只是可读性差一点,并且调试的时候麻烦点。但是这样很常见。

      let z = sum(4, sum(5,6))
    

Void返回类型和参数

  • 函数可以没有返回值。可以各种省略。如果没有返回值,则函数体里面可以没有return语句。有return的话,仅仅是停止函数。

      func say1(_ s:String) -> Void {...}
      func say2(_ s:String) -> () {...}
      func say3(_ s: String) {...}
    
  • 函数也可以没有参数。写的时候不用写参数,但是参数的括号不能省略。调用的时候也不能省略。

  • 形式上函数没有返回任何东西,实际上还是返回了Void类型的的值。

函数签名

  • 函数签名表达式,表明函数的输入输出,结构。函数签名实际上就是函数的类型。
  • 函数签名要包含函数参数列表和返回值类型。即使说都是空的。
() -> () //空参数列表,返回值也为空

外部参数名

  • 函数可以外部化参数名字。调用的时候就需要作为标签标示参数。这样可以:
    • 表明每个参数的目的
    • 区分不同的函数
    • 兼容Objective-C和Cocoas
  • 这是swift的标准。默认情况下,所有的参数都外部化,外部名字和内部名字是一样的。所以一般啥也不用干。
  • 如果你想要的和默认表现不一样,有两种情况
    • 修改外部参数名(相应的地方改名字)
    • 废除外部参数化(用下划线)
  • 外部化的时候,调用的时候也要把外部化的参数给到。虽然有外部参数,但是参数的顺序是不能变的。

重载

  • swift中重载是可以的,而且非常普遍。函数签名不同,重载是可以的,因为swift是强类型语言,可以区分不同的函数。
  • 函数返回值不同的函数也可以重载,同时存在。但是调用的时候必须要能够明确区分。
  • 因为Objective-C是不支持重载的。所以如果你定义了swift的重载函数,而OC又能看见,这是不允许的。因为和OC不兼容。
  • 有不同外部参数名的函数不认为是重载。外部参数名作为函数名字的一部分,如果外部参数名不同,认为是不同的函数。

默认参数值

  • 参数可以有默认值。就是说如果没有传入实参,则可以忽略整个参数,用默认值。但是不能和函数重载冲突。

      //定义这样的形式
      func say(_ s: String, times: Int = 1){
          for _ in 1...times {
              print(s)
          }
      }
      //调用这样的形式
      say("haha")
    

可变参数

  • 可以用... 放参数列表中,表示可变参数
func sayStrings(_ arrayOfStrings:String ...){
    for s in arrayOfStrings{ print(s) }
}
sayStrings("haha","hoho","huhu")
  • swift中没有把数组转换成为参数列表的方法。

被忽略的参数

  • 参数内参名为下划线的参数是被忽略的参数。这个参数在函数体中没有用到,所以也没有名字。但是,在调用的时候依然需要提供这个参数。
  • 被忽略的参数也可以没有外部参数名
  • 这么做不是为了让编译器满意。是为了提醒。

可修改的参数

  • 函数参数本质上是一个本地变量,隐含的是let的,即不可修改的。* 当然你可以再声明一个同名的本地变量,然后把参数的值传给这个本地变量。要想变成可改的:
    • 参数类型要定义成inout
    • 传入到里面的参数必须是var类型的,不能是let类型的
    • 必须以地址的方式传入
      & 符号显示的告诉我们,传入的参数可能有潜在的影响。会被修改。
  • 在和OC交互的时候,经常遇见类似UnsafeMutablePointer这样的东西,和inout类似,都是地址传递。
  • 类类型传入到函数内部的时候,是可以修改类内部的值的。不用使用inout这样的标示。
  • 通常类对象是引用类型,字符串对象等是值类型。

函数内部的函数

  • 函数的定义可以在任何地方。包括函数体内部。函数体内部定义的函数,只能在函数体作用域范围内使用,其他地方看不到。如果以函数只被另一个函数调用,那么被调用的那个函数就应该被包含在调用函数里面。
  • 有时候为了代码的可读性,即使是仅有一个地方调用,也可以使用函数的形式。
  • 本地函数不能和在同一个作用域的本地变量有同样的名字。也不能和同一个作用域的其他函数冲突。

递归

  • 函数调用函数本身,就是递归。但是要注意递归退出条件。

函数作为值

  • 函数是swift中的一等公民。函数可以用在任何可以传值的地方
  • swift是强类型的语言,赋值需要类型一致。函数的类型就是函数的签名。
  • 函数作为值传递,是为了函数在之后调用,而不关心具体的函数。
  • 包含一个函数在另一个函数里,可以减少重复以及发生错误的几率。把变化的东西搞成一个函数,以参数的形式传入即可。
  • 可以用typealiase声明函数类型。这样可以更加清晰的使用函数。毕竟,函数签名的表达方式太凌乱了。
  • Cocoa常常有这样的函数传入,回调。OC中的block回调。handle。

匿名函数

  • 传入函数作为参数的时候,如果函数仅仅是在调用的地方使用,那么函数名就是没必要的了。因此可以直接传入函数体。
    • 创建函数体。包括大括号。但是不需要函数名。
    • 函数参数和返回值如果有的话,放在函数体的第一行。用 in 标示。
UIView.animate(withDuration:0.4,
    animations :{
        ()->() in
        self.myButton.frame.origin.y += 20
    },
    completion:{
         (finished:Bool) -> () in
          print("finished: \(finished)")
    }
)
  • 匿名函数还有很多省略的形式
    • 如果编译器知道函数返回值类型,则可以不写箭头和返回值类型。
    • 如果没有参数的话,而返回值又可知的话,可以直接省略in这行。
    • 省略参数类型。如果参数类型编译器知道,则可以省略。
    • 省略括号。如果忽略了参数类型,那么参数列表的括号也可以省略。
    • 省略in整行。如果有参数,可以直接用$0,$1代替。
    • 可以省略参数名字。如果参数没有被引用,则可以用下划线标示参数名。但是你需要告诉编译器有他们,也就是说,你可以不写in行直接用$0,$1之类的,也可以下划线in,但是不能同时都省略,这样编译出错。
    • 省略函数参数的label。如果匿名函数是最后一个参数,可以先写函数右括号,然后带着匿名函数的函数体在最后。不写label了。这叫做trailing function
    • 省略调用函数的括号。如果是trailing function的情况,并且调用的函数除了传入的函数之外没有别的参数,那么可以省略函数调用时候的括号。直接写函数体。看似函数定义的形式,其实是函数调用。这也是函数调用唯一可以省略小括号的地方。
    func doThis(_ f:() ->()){
        f()
    }

    // 忽略括号,这里是函数调用,不是函数定义
    doThis{
        print("Howdy")
    }

    • 省略关键词return。如果函数体只有一行语句并且语句中有值。那么return可以省略。swift会假定这个语句表达式的值就是返回值。
  • 可以充分利用匿名函数的省略特性。同时你经常缩写布局,这样更加紧凑。

let arr = [2,4,6,8]
func doubleMe(i: Int) -> Int{
    return i * 2
}
// 原始的写法
let arr2 = arr.map(doubleMe)

// 更加swift的写法
let arr2 = arr.map({
    (i: Int) -> Int in 
    return i * 2
})

// 非常swift的写法
let arr2 = arr.map{$0 * 2}

定义和调用

  • 可以定义一个匿名函数然后立刻调用它。注意后面的括号。
  • 为啥有这种玩法?可以把一块东西放在一起。而不是比如放在一堆准备工作的位置。比如造对象的时候。

闭包

  • swift的函数就是闭包。可以获取外部的变量到内部来使用。
  • 当外部环境变了,依然可以引用到外部变量。当函数被作为值传入另一个函数的时候,也带着函数依赖的周围变量一起。

闭包怎样改进代码

  • 闭包可以使你的函数更通用,更有用。
    // 原本函数是这样的
     func imageOfSize(_ size:CGSize, _ whatToDraw:()->()) -> UIImage {
        UIGraphicsBeginImageContextWithOptions(size,false,0)
        whatToDraw()
        let result = UIGraphicsGetImageFromCurrentImageContext()!
        UIGraphicsEndImageContext()
        return result
    }

    // 我们可以这样调用
    let sz = SGSize(width:45,height:20)
    let image = imageOfSize(sz) {
        let p = UIBesierPath(roundeedRect:sz,cornerRadius:8)
      p.stroke()
    }
    //然后包起来
func makeRoundedRectangle(_ sz:SGSize) -> UIImage {
    let image = imageOfSize(sz){
        let p = UIBesierPath(
            roundedRect:CGRect(origin:CGPoint.zero,size:sz)
            cornerRadius:8
        )
        p.stroke()
    }
}

函数返回函数

  • (_ sz:CGRect) -> () ->UIImage 类似这样的是说返回一个函数类型。其中函数类型是()->UIImage
func makeRoundedRectanglemaker(_ sz:CGRect) -> () -> UIImage {
  return {
      imageOfSize(sz) {
        let p = UIBezierPath(
            roundedRect:CGRect(origin:CGPoint.zero, size:sz)
            cornerRadius:8
        )
        p.stroke()
      }
  }
}

闭包设置一个捕获变量

  • 闭包捕获了一个变量,如果这个变量可以设置,那么闭包里面可以更改这个变量。

闭包保护捕获环境

  • 闭包会把环境中的变量保存下来,返回之后如果函数不释放,那么捕获的变量也不会释放。

Escaping 闭包

  • 如果一个函数作为参数传递给另一个函数,并且延迟执行,这个函数参数必须标示成 @escaping。
  • 如果一个匿名函数传了一个@escaping类型的参数,引用了self的函数或方法,那么你必须明确指定self。

柯里化函数

可以返回带参数的函数,外部调用的时候,参数一个个传下去。

func makeRoundedRectangleMaker(_ sz:CGSize) -> (CGFloat) -> UIImage {
   return {
      r in
      imageOfSize(sz){
        let p = UIBezierPath(
          roundedRect:CGRect(origin:CGPoint.zero, size:sz),
          cornerRadius:r)
        p.stroke()
      }
  }

}

函数引用和Selectors

  • 函数引用一般使用函数名引用,然后可以调用。也就是简单名称。一般不会有歧义。
  • 在把函数赋给一个变量的时候,比如如果有重载,则会发现仅仅使用函数名会有歧义。所以需要用以下的方法来处理:
    • 使用函数全名。全名则包括函数名和外部参数名。
    • 使用函数签名。带着返回值,输入输出类型。有的时候函数全部不够明确,比如没有参数的函数。全名一样。这时候使用函数签名就能指明明确的函数。

函数引用范围

  • 有时候函数引用可以提供更多关于函数在哪里定义的信息。这样告诉编译器是引用的哪个函数。有时候编译器会强制要求你用self来指示是哪个函数。
  • 有时候可以用类名+点+方法名来引用一个函数,即使这个方法是实例方法。

Selectors

  • OC中有selector,是方法的引用。但是这个东西经常引入错误。尤其是需要写正确里面的方法字符串。如果写错了,就会在运行的时候告诉你找不到方法。直接crash。
  • 在swift中,可以用#selector(...)来代替这种方法。一方面,编译器会检测字符串的方法名是不是正确,如果不正确会编译不通过。另外,编译器会帮你生成正确的selector。
  • 偶尔你可能还是要用到oc的方式。
  • swift的方式不能保证你绝对不会crash。比如你target写错了的时候,swift的方式就检查不出来。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,254评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,875评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,682评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,896评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,015评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,152评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,208评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,962评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,388评论 1 304
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,700评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,867评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,551评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,186评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,901评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,142评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,689评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,757评论 2 351

推荐阅读更多精彩内容

  • 86.复合 Cases 共享相同代码块的多个switch 分支 分支可以合并, 写在分支后用逗号分开。如果任何模式...
    无沣阅读 1,356评论 1 5
  • 2014年的苹果全球开发者大会(WWDC),当Craig Federighi向全世界宣布“We have new ...
    yeshenlong520阅读 2,278评论 0 9
  • Hello Word 在屏幕上打印“Hello, world”,可以用一行代码实现: 你不需要为了输入输出或者字符...
    restkuan阅读 3,173评论 0 6
  • 这是一个真实的故事,请不要当作一个小说来阅读。 草原上一个偏寂的小村,小的可怜,只有零星的几户人家。就在这个小村,...
    乐搞吧阅读 272评论 0 0
  • 想象中的园博园,没有幽蓝的湖水,也没有娴雅苍翠的青山,更没有俏丽轻盈的客船和环湖缠绵的小路。 我以为它不过就是一片...
    花开的时间阅读 912评论 2 3