美文网首页
读书笔记-Swift-02函数

读书笔记-Swift-02函数

作者: 秋水天的练功房 | 来源:发表于2017-08-26 17:31 被阅读0次

    函数参数和返回值

    • 函数就是这么个玩意:加一些材料进去,通过一个过程,然后产出一些东西。放进去的就是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的方式就检查不出来。

    相关文章

      网友评论

          本文标题:读书笔记-Swift-02函数

          本文链接:https://www.haomeiwen.com/subject/fwcvdxtx.html