美文网首页
iOS-Swift-函数

iOS-Swift-函数

作者: Imkata | 来源:发表于2020-01-03 09:33 被阅读0次

    一. 函数的定义

    • 无返回值
    无返回值
    • 有返回值

    形参默认是let,也只能是let

    有返回值

    注意:Swift中可以使⽤func定义⼀个函数,也可以使⽤闭包表达式定义⼀个函数。

    • 隐式返回(Implicit Return)

    如果整个函数体是一个单一表达式,那么函数会隐式返回这个表达式

    隐式返回
    • 返回元组:实现多返回值
    返回元组

    二. 函数的文档注释

    格式如下:

    函数的文档注释

    尽量遵守注释,这样按住optional会有提示:

    提示

    更多关于函数文档注释可参考:https://swift.org/documentation/api-design-guidelines/

    三. 参数

    1. 参数标签(Argument Label)

    默认参数标签是参数名,如果设置了参数标签会覆盖默认标签。

    参数标签

    设置参数标签是为了可读性,使函数调用读起来更像个英文句子。
    上面的at是为了读起来更通顺,上面的time是为了让你理解传进来的参数是什么。

    可以使用下划线 _ 省略参数标签。

    _省略参数标签

    不推荐省略参数标签,因为有时候省略了就不知道传的是哪个参数了,如下:

    不推荐省略参数标签

    2. 默认参数值(Default Parameter Value)

    参数可以有默认值,调⽤带有默认参数的函数时,带有默认参数的参数可以不传,其他参数⼀定要传。

    默认参数

    C++的默认参数值有个限制:必须从右往左设置。由于Swift拥有参数标签,因此并没有此类限制。

    3. 可变参数(Variadic Parameter)

    一个函数最多只能有1个可变参数。

    可变参数

    紧跟在可变参数后面的参数不能省略参数标签,因为会有歧义。

    不能省略
    • Swift自带的print函数
    print

    可以发现,print函数传三个参数
    第一个参数是可变参数,省略了标签。
    第二个参数是分隔符,默认是空格。
    第三个参数是两个打印之间的结束符,默认是以换行结束。

    如果需要给第二个第三个参数传值,那么第二个第三个参数的标签不能省略标签。

    4. 输入输出参数(In-Out Parameter)

    可以用inout定义一个输入输出参数:可以在函数内部修改外部实参的值。

    输入输出参数

    输入输出函数调用的时候需要加&

    其实用于交换外部实参的值更简单的方法是使用元祖,如下:

    使用元祖

    注意:
    可变参数不能标记为inout
    inout参数不能有默认值
    inout参数只能传入可以被多次赋值的
    inout参数的本质是地址传递(引用传递)

    inout参数只能传入可以被多次赋值的是什么意思?
    如果传入的是字面量40或者let修饰的参数,字面量不能改,let修饰的参数只能赋值一次,所以如果传入字面量40或者let修饰的参数就达不到在函数内部可以修改的目的,所以不能传入。

    注意:
    上面所示代码中,inout参数的本质的确是地址传递(引用传递)。
    如果传递给inout参数的是计算属性、有监听器的属性等内容,其本质并非引用传递,在属性章节再作详细介绍。

    其实苹果提供了用于交换的函数swap,上面为了不跟苹果提供的函数冲突才起名swapValues。

    • 验证inout参数的本质是地址传递

    MJ老师是通过汇编验证的,我看不懂,直接查看苹果提供的swap函数源码:

    @inlinable
    public func swap<T>(_ a: inout T, _ b: inout T) {
      // Semantically equivalent to (a, b) = (b, a).
      // Microoptimized to avoid retain/release traffic.
      let p1 = Builtin.addressof(&a)
      let p2 = Builtin.addressof(&b)
      _debugPrecondition(
        p1 != p2,
        "swapping a location with itself is not supported")
    
      // Take from P1.
      let tmp: T = Builtin.take(p1)
      // Transfer P2 into P1.
      Builtin.initialize(Builtin.take(p2) as T, p1)
      // Initialize P2.
      Builtin.initialize(tmp, p2)
    }
    

    很容易看出的确是地址传递。

    四. 函数重载(Function Overload)

    函数重载的规则:
    1.函数名相同
    2.参数个数不同 || 参数类型不同 || 参数标签不同

    重载和重写不一样,重载牵扯不到继承,重写牵扯到继承。

    函数重载

    上面都能构成函数重载,调用结果如下:

    函数重载调用结果
    • 函数重载注意点

    返回值类型与函数重载无关,例如下面不构成函数重载。

    不构成函数重载

    默认参数值和函数重载一起使用产生二义性时,编译器并不会报错(在C++中会报错)

    二义性

    可变参数、省略参数标签、函数重载一起使用产生二义性时,编译器有可能会报错

    二义性报错

    五. 内联函数(Inline Function)

    内联函数就是将函数调用展开成函数体。

    如果开启了编译器优化(Release模式默认会开启优化),编译器会自动将某些函数变成内联函数。

    开启编译器优化步骤如下:

    编译器优化

    哪些函数不会被自动内联?
    函数体比较长
    包含递归调用
    包含动态派发
    ......

    什么是动态派发?
    动态派发类似于OC的多态,就是父类指针指向子类对象,在编译时并不知道要调用的是父类还是子类的方法,只有在运行的时候才能知道实际调用哪个方法。

    编译器将某些函数变成内联函数,是在编译时期,但是在编译时期无法确定调用哪个函数,所以包含动态派发不会被自动内联。

    • @inline
    @inline

    在Release模式下,编译器已经开启优化,会自动决定哪些函数需要内联,因此没必要使用@inline

    六. 函数类型(Function Type)

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

    比如下面的函数,函数类型分别是右边的注释:

    函数类型

    注意:Void 其实就是 () 空元祖

    • 函数类型作为函数参数
    函数类型作为函数参数
    • 函数类型作为函数返回值
    函数类型作为函数返回值

    返回值是函数类型的函数,叫做高阶函数(Higher-Order Function)

    • typealias

    typealias用来给类型起别名,Swift中没有Byte、Short、Long,我们可以通过起别名,定义一个:

    Byte、Short、Long

    其中Int8一个字节,Int16两个字节,Int64八个字节。

    也可以给元祖起别名:

    给元祖起别名

    给函数类型起别名:

    给函数类型起别名

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

    public typealias Void = ()
    

    所以,当函数返回值为空的时候,返回值可以什么都不写,也可以写Void,也可以写()。

    • 嵌套函数(Nested Function)

    将函数定义在函数内部

    嵌套函数

    因为next和previous函数只在forward函数里面使用到,所以可以把next和previous函数定义在forward函数里面。

    相关文章

      网友评论

          本文标题:iOS-Swift-函数

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