美文网首页
★ 学习笔记:《iOS高级:Swift入门精讲②》第一节 Swi

★ 学习笔记:《iOS高级:Swift入门精讲②》第一节 Swi

作者: 麦穗0615 | 来源:发表于2019-07-25 23:41 被阅读0次

    前言:

    本篇仅为视频学习笔记

    函数类型 (Function Type)

    ★每一个函数都是有类型的,函数类型有形式参数类型、返回值类型组成

    例子 -1

    func test() { } // () -> Void 或者() -> ()
    

    我们看上边这个函数,其实它的类型就是() -> Void,其中()是参数列表 Void它是返回值,由于它没有参数,所以写成(),返回值写一个Void。Void等价于(),所以函数的类型也可以写成() -> ()。综上,函数的类型可以写成() -> Void或者() -> ()。


    我们再看下一个。

    例子 -2

     func sum(a: Int, b: Int) -> Int {
         return a + b
     } // (Int,Int)-> Int
    

    它的函数类型是什么呢?是 (Int,Int)-> Int。这样一看,就会明白,它呢,接收了两个Int类型的参数,返回值类型为Int类型


    我们再看下一个。

    例子 -3

     // 定义变量
     var fn: (Int,Int) -> Int = sum
     fn(2,3) // 5,调用时不需要参数标签
    

    我们可以定义变量,定义一个fn变量,那么这个变量的类型是一个函数类型,既然你这个变量是(Int,Int) -> Int类型,我们就可以赋值一个函数名给fn。当然你赋值的函数名必须符合这个类型。假如呢,你把例子1中的test赋值给fn的话,那肯定会报错。因为sum刚好是这种类型的,所以sum这个函数名可以赋值给fn。那么赋值之后呢,我们就可以利用这个变量去调用这个函数。一调用,就把2和3传递给例2中的a和b了。到时候,就会执行函数sum,内部函数体代码。

    像利用变量去调用一个函数的话,那么参数标签是不需要写的。相当于以前,你调用sum将函数的时候,是要写a和b这两个标签的。但是一旦传给一个变量,你就直接写2和3,它会按顺序传递给他们(a和b)。

    函数类型作为函数参数

     func sum(v1: Int, v2: Int) -> Int {
         return v1 + v2
     }
     
     func difference(v1: Int, v2: Int) -> Int {
          return v1 - v2
     }
     
     func printResult(_ mathFn:(Int,Int) -> Int, _ a: Int, _ b: Int) {
         print("Result: \(mathFn(a,b))")
     }
     
     printResult(sum, 5, 2) // Result: 7
     printResult(difference, 5, 2) // Result: 3
    

    我们来看一个问题,我们定义了两个函数,一个是做加法的,一个是减法的,然后我们又定义了一个函数printResult来打印结果。函数printResult接收了三个参数,而且这三个参数都写了下划线,那么调用的时候,就不用写标签了。

    第一个参数是函数类型,说白了第一个参数要求你传一个(Int,Int) -> Int这种类型的函数,然后第二个、第三个参数传Int。那你思考一下,我们是不是可以这样传呢?

     printResult(sum, 5, 2) // Result: 7
     printResult(difference, 5, 2) // Result: 3
    

    因为我们这个sum函数跟difference这两个函数是不是就是(Int,Int) -> Int这种类型的。所以呢,可以这样传。

    那么,传递进去,我会怎么做呢?我会把你传进来的这个函数调用一下,把a和b传到mathFn(a,b)这个函数里。说白了,如果我传的是这个 printResult(sum, 5, 2) ,那么就是把5和2传到了sum函数里去调用。printResult(difference, 5, 2)这个是将5和2传递到difference函数里去调用。并且把这个结果,利用这个斜线小括号放到字符串中打印出来,所以最后打印出来printResult(sum, 5, 2)结果是Result: 7。 printResult(difference, 5, 2)结果是Result: 3。甚至以后,将函数变成一个属性。

    甚至以后,将函数变成一个属性。你看下面代码:


    以后我们在写一个类的时候,这个类的属性,也就是大家所认为的成员变量,是不是可以有Int类型,也可以有函数类型。到时候可以将一个函数赋值给它。

    函数类型作为函数返回值

    当然,既然是类型也可以作为函数的返回值类型。比如说举个例子如下:

     func next(_ input: Int) -> Int {
        return input + 1
     }
     
     func previous(_ input: Int) -> Int {
         return input - 1
     }
     
     func forward(_ forward: Bool) -> (Int) -> Int {
         return forward ? next : previous
     }
     
     forward(true)(3)  // 4
     forward(false)(3) // 2
    

    我这个next函数,是你传一个东西给我,我就往前走就是加1。previous函数就是你传一个东西给我,我就往后走减1。这样说吧,next是上一步,previous是下一步。上一步就加1,下一步就减1。

    因为函数体,就一个表达式,可以不用写return这个家伙。不写的时候,相当于直接return这个家伙。

    forward函数,是你传一个Bool布尔类型给我,我来决定返回哪一个函数给你。你会发现forward函数的返回值是一个函数** (Int) -> Int**。这里千万不要搞晕了。

    首先,这一个这一个符号,是 forward函数的返回值的开头,右边那个就是返回值类型,说白了,它返回的是一个函数。然后,这个函数要求接收一个Int类型参数,返回一个Int类型。

    很明显 next函数和previous函数都是符合条件的。它们都是接收一个Int,返回一个Int。所以注意看,forward函数中,你传递一个布尔类型Bool给我,我发现这个布尔类型Bool是true,我就返回next函数,否则返回previous这个函数。


    所以呢,大家注意看一个问题。到时候,我调用forward这个函数的时候,如果你传一个true进来,我返回的是next函数。所以这个next函数一调用,传一个3的话,就会赋值给next函数中input这个标签,去执行函数体代码。所以3+1 = 4

    传一个false,就会返回previous函数,将3传给previous函数中的input这个标签,去执行函数内部代码,其实就是 3 -1,结果为2。


    再说一个概念
    ★ 返回值是函数类型的函数,叫做高阶函数(Higher-Order Function)

    typealias

    来看一个关键字,typealias这个关键词是什么意思呢?alias是起别名的意思。type是类型,说白了他是给类型,起别名的。

    ★ typealias用来给类型起别名

    那么怎么起别名呢?

     typealias Byte = Int8
     typealias Short = Int16
     typealias Long = Int64
    

    其实我们Swift里面是没有Byte、Short、Long这三个类型的,但是我们可以自己给它们创造一下。

    我们思考一下, Byte不就代表一个字节吗?8位。我们可以来一个Int8,Int8不就是代表一个8位的Int类型吗?这样做就可以了。这样就给Int8起了一个别名,叫做Byte。我觉得这个也很好理解,就像将右边Int8赋值给了左边Byte,说白了Byte就等价于Int8。

    Short不就是代表两个字节的Int类型吗?我们可以直接将Int16赋值给它(Short)。Long一般代表8个字节,也就是64位的,所以我们将Int64赋值给它(Long)。那么这样,就相当于,我们创造了三个类型吗?


    这个起别名,我们给元祖也是可以的。

     typealias Date = (year: Int, momth: Int, day: Int)
     
     func test(_ date: Date) {
         print(date.0)
         print(date.year)
     }
     test((2011,9,10))
    

    我们⚠️注意观察,我现在是给元祖取了一个别名,我们将右边的元祖类型(year: Int, momth: Int, day: Int)赋值给了左边的 Date,所以以后用这个 Date就代表了右边的元祖类型 (year: Int, momth: Int, day: Int)

    所以,你注意看 test 函数是接收一个元祖类型 Date,到时候,你思考一下,到时候是不是可以传这个元祖test((2011,9,10))啊。(2011,9,10)中2011、9、10,很明显就是 (year: Int, momth: Int, day: Int)这三个东西。

    我将元祖(2011,9,10)传进test函数去了,赋值给了date。拿到元祖,执行函数体代码,我们用点0就date.0可以,访问最前面的成员。也可以通过这个名称year标签,访问到最前面的成员。


    然后,我们还可以怎么样呢?给函数类型起一个别名。

    总之,你右边能放什么东西呢?类型就行。你可以将右边的类型赋值给左边。既然是放类型,函数类型也是一种类型。如下:

     typealias IntFn  = (Int,Int) -> Int
     
     func difference(v1: Int, v2: Int) -> Int {
          return v1 - v2
     }
     
     let fn: IntFn = difference
     fn(20,10) // 10
     
     func setFn(_ fn: IntFn) {
         setFn(difference)
     }
     
     func getFn() -> IntFn {
         return difference
     }
    

    所以, typealias IntFn = (Int,Int) -> Int 这样写是什么意思呢?你可以用IntFn代表(Int,Int) -> Int 这种函数类型。

    那你思考一下,很明显这个 difference函数,就是符合IntFn这种类型的,也就是说白了difference就是符合IntFn这种类型的。

    所以,到时候我可以,定义一个IntFn类型的常量fn,然后来存储difference值。这样调用是成功的 fn(20,10) // 10。

    到时候,传参也是一样的,我们看setFn函数,要求你传进来的类型是IntFn,那明显difference是符合这个类型的。所以,可以这样传进去 setFn(difference)。

    然后,返回值也是一样的。我们getFn,要求返回IntFn类型,所以我这个difference是符合这个类型的,继而返回difference。

    按照Swift标准库的定义,Void就是空元祖()

    那么什么意思呢?之前提到过,如果你想表达,一个函数没有返回值的话,有三种做法。

    第一种,什么也不写,这个右边

     func test() {
         
     }
    

    第二种,右边可以写一个 -> Void

      func test() -> Void {
          
      }     
    

    第二种,右边可以写一个 -> () 空元祖

     func test() -> () {
         
     }        
    

    那么,为什么这么肯定呢,原因很简单。因为,如果,你去看一下Void这个定义你就会发现,在Swift标准库中有这么一句:

      public typealias Void = ()
    

    所以,Void和()是等价的,当然你也可以自己进去看一下。


    相关文章

      网友评论

          本文标题:★ 学习笔记:《iOS高级:Swift入门精讲②》第一节 Swi

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