Swift官方文档阅读笔记(函数、闭包、枚举、类和结构体、属性、

2018-08-03  本文已影响15人  九龙

Swift中文文档连接

函数相关

可变形式参数

一个可变形式参数可以接受零或者多个特定类型的值。当调用函数的时候你可以利用可变形式参数来声明形式参数可以被传入值的数量是可变的。可以通过在形式参数的类型名称后边插入三个点符号( ...)来书写可变形式参数。

传入到可变参数中的值在函数的主体中被当作是对应类型的数组。举个栗子,一个可变参数的名字是 numbers类型是 Double...在函数的主体中它会被当作名字是 numbers 类型是 [Double]的常量数组。

下面的栗子计算了一组任意长度的数字的算术平均值(也叫做平均数)。

  func arithmeticMean(_ numbers: Double...) -> Double {
    var total: Double = 0
    for number in numbers {
        total += number
    }
    return total / Double(numbers.count)
}
arithmeticMean(1, 2, 3, 4, 5)
// returns 3.0, which is the arithmetic mean of these five numbers
arithmeticMean(3, 8.25, 18.75)
// returns 10.0, which is the arithmetic mean of these three numbers

注意
一个函数最多只能有一个可变形式参数。

输入输出形式参数

就像上面描述的,可变形式参数只能在函数的内部做改变。如果你想函数能够修改一个形式参数的值,而且你想这些改变在函数结束之后依然生效,那么就需要将形式参数定义为输入输出形式参数。

在形式参数定义开始的时候在前边添加一个 inout关键字可以定义一个输入输出形式参数。输入输出形式参数有一个能输入给函数的值,函数能对其进行修改,还能输出到函数外边替换原来的值。

你只能把变量作为输入输出形式参数的实际参数。你不能用常量或者字面量作为实际参数,因为常量和字面量不能修改。在将变量作为实际参数传递给输入输出形式参数的时候,直接在它前边添加一个和符号 ( &) 来明确可以被函数修改。

注意

输入输出形式参数不能有默认值,可变形式参数不能标记为 inout,如果你给一个形式参数标记了 inout,那么它们也不能标记 var和 let了。

这里有一个 swapTwoInts(::)函数,它有两个输入输出整数形式参数 a和 b:

func swapTwoInts(_ a: inout Int, _ b: inout Int) {
    let temporaryA = a
    a = b
    b = temporaryA
}

函数 swapTwoInts(::)只是简单的将 b和 a的值进行了调换。函数将 a的值储存在临时常量 temporaryA中,将 b的值赋给 a,然后再将 temporaryA的值赋给 b。

你可以通过两个 Int类型的变量来调用函数 swapTwoInts(::)去调换它们两个的值,需要注意的是 someInt的值和 anotherInt的值在传入函数 swapTwoInts(::)时都添加了和符号。

var someInt = 3
var anotherInt = 107
swapTwoInts(&someInt, &anotherInt)
print("someInt is now \(someInt), and anotherInt is now \(anotherInt)")
// prints "someInt is now 107, and anotherInt is now 3"

上边的栗子显示了 someInt 和 anotherInt的原始值即使是在函数的外部定义的,也可被函数 swapTwoInts(::)修改。

注意

输入输出形式参数与函数的返回值不同。上边的 swapTwoInts没有定义返回类型和返回值,但它仍然能修改 someInt和 anotherInt的值。输入输出形式参数是函数能影响到函数范围外的另一种替代方式。


闭包相关

涉及关键字:尾随闭包,逃逸闭包 @escaping,自动闭包 @autoclosure 闭包是引用类型
https://www.cnswift.org/closures


枚举相关

关联值

有时将其它类型的关联值与这些成员值一起存储是很有用的。这样你就可以将额外的自定义信息和成员值一起储存,并且允许你在代码中使用每次调用这个成员时都能使用它。
你可以定义 Swift 枚举来存储任意给定类型的关联值,如果需要的话不同枚举成员关联值的类型可以不同。
在Moya里面的使用场景
例如:

enum Barcode {
    case upc(Int, Int, Int, Int)
    case qrCode(String)
}

这可以读作:
“定义一个叫做 Barcode的枚举类型,它要么用 (Int, Int, Int, Int)类型的关联值获取 upc 值,要么用 String 类型的关联值获取一个 qrCode的值。”

递归枚举

关键字:indirect

示例:

indirect enum ArithmeticExpression {
    case number(Int)
    case addition(ArithmeticExpression, ArithmeticExpression)
    case multiplication(ArithmeticExpression, ArithmeticExpression)
}

let five = ArithmeticExpression.number(5)
let four = ArithmeticExpression.number(4)
let sum = ArithmeticExpression.addition(five, four)
let product = ArithmeticExpression.multiplication(sum, ArithmeticExpression.number(2))

类和结构体

类与结构体的对比

在 Swift 中类和结构体有很多共同之处,它们都能:

类有而结构体没有的额外功能:

注意

结构体在你的代码中通过复制来传递,并且并不会使用引用计数。

结构体和枚举是值类型

值类型是一种当它被指定到常量或者变量,或者被传递给函数时会被拷贝的类型。

let hd = Resolution(width: 1920, height: 1080)
var cinema = hd
cinema.width = 2048
println("cinema is now \(cinema.width) pixels wide")
  //println "cinema is now 2048 pixels wide"
print("hd is still \(hd.width) pixels wide")
// prints "hd is still 1920 pixels wide"

类是引用类型

类和结构体之间的选择

按照通用准则,当符合以下一条或多条情形时应考虑创建一个结构体:

合适的结构体候选者包括:


属性

注意

  • 如果被标记为 lazy 修饰符的属性同时被多个线程访问并且属性还没有被初始化,则无法保证属性只初始化一次。
  • 全局常量和变量永远是延迟计算的,与延迟存储属性有着相同的行为。不同于延迟存储属性,全局常量和变量不需要标记 lazy 修饰符。

方法

在实例方法中修改值类型

关键字:mutating

    var x = 0.0, y = 0.0
    mutating func moveBy(x deltaX: Double, y deltaY: Double) {
        x += deltaX
        y += deltaY
    }
}
var somePoint = Point(x: 1.0, y: 1.0)
somePoint.moveBy(x: 2.0, y: 3.0)
print("The point is now at (\(somePoint.x), \(somePoint.y))")
// prints "The point is now at (3.0, 4.0)"

//你不能在常量结构体类型里调用异变方法,因为自身属性不能被改变,就算它们是变量属性:
let fixedPoint = Point(x: 3.0, y: 3.0)
fixedPoint.moveBy(x: 2.0, y: 3.0)
// this will report an error

在异变方法里指定自身

异变方法可以指定整个实例给隐含的 self属性。上文中那个 Point的栗子可以用下边的代码代替:

struct Point {
    var x = 0.0, y = 0.0
    mutating func moveBy(x deltaX: Double, y deltaY: Double) {
        self = Point(x: x + deltaX, y: y + deltaY)
    }
}

初始化

类类型的初始化器委托

为了简化指定和便捷初始化器之间的调用关系,Swift 在初始化器之间的委托调用有下面的三个规则:

简单记忆的这些规则的方法如下:

两段式初始化

Swift 的类初始化是一个两段式过程。在第一个阶段,每一个存储属性被引入类为分配了一个初始值。一旦每个存储属性的初始状态被确定,第二个阶段就开始了,每个类都有机会在新的实例准备使用之前来定制它的存储属性。

两段式初始化过程的使用让初始化更加安全,同时在每个类的层级结构给与了完备的灵活性。两段式初始化过程可以防止属性值在初始化之前被访问,还可以防止属性值被另一个初始化器意外地赋予不同的值。

注意

Swift 的两段式初始化过程与 Objective-C 的初始化类似。主要的不同点是在第一阶段,Objective-C 为每一个属性分配零或空值(例如 0 或 nil )。Swift 的初始化流程更加灵活,它允许你设置自定义的初始值,并可以自如应对 0 或 nil 不为合法值的情况。

Swift编译器执行四种有效的安全检查来确保两段式初始化过程能够顺利完成:

安全检查 1

指定初始化器必须保证在向上委托给父类初始化器之前,其所在类引入的所有属性都要初始化完成。

如上所述,一个对象的内存只有在其所有储存型属性确定之后才能完全初始化。为了满足这一规则,指定初始化器必须保证它自己的属性在它上交委托之前先完成初始化。

安全检查 2

指定初始化器必须先向上委托父类初始化器,然后才能为继承的属性设置新值。如果不这样做,指定初始化器赋予的新值将被父类中的初始化器所覆盖。

安全检查 3

便捷初始化器必须先委托同类中的其它初始化器,然后再为任意属性赋新值(包括同类里定义的属性)。如果没这么做,便捷构初始化器赋予的新值将被自己类中其它指定初始化器所覆盖。

安全检查 4

初始化器在第一阶段初始化完成之前,不能调用任何实例方法、不能读取任何实例属性的值,也不能引用 self 作为值。

直到第一阶段结束类实例才完全合法。属性只能被读取,方法也只能被调用,直到第一阶段结束的时候,这个类实例才被看做是合法的。

以下是两段初始化过程,基于上述四种检查的流程:

阶段 1
阶段 2

自动初始化器的继承

子类默认不会继承父类初始化器。总之,在特定的情况下父类初始化器是可以被自动继承的。实际上,这意味着在许多场景中你不必重写父类初始化器,只要可以安全操作,你就可以毫不费力地继承父类的初始化器。

假设你为你子类引入的任何新的属性都提供了默认值,请遵守以下2个规则:

规则1

规则2

就算你的子类添加了更多的便捷初始化器,这些规则仍然适用。

上一篇 下一篇

猜你喜欢

热点阅读