欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Swift-基础部分

程序员文章站 2024-02-20 15:15:40
...

1. 常量和变量

常量的值一旦设定就不能改变,而变量的值可以随意更改。

1.1 声明常量和变量

  • 常量和变量必须在使用前声明。
  • 用 let 来声明常量。
  • 用 var 来声明变量。
let a = 10
var b = 0
// 可以在一行中声明多个常量或者多个变量,用逗号隔开:
var x = 0.0, y = 0.0, z = 0.0

1.2 类型注解

  • 当你声明常量或者变量的时候可以加上类型注解(type annotation),说明常量或者变量中要存储的值的类型。
// 声明一个类型为 String ,名字为 message 的变量
var message: String
// 可以在一行中定义多个同样类型的变量,用逗号分割,并在最后一个变量名之后添加类型注解
var red, green, blue: Double

1.3 输出常量和变量

let age = 10
let name = "jelly"
// Swift 用字符串插值(string interpolation)的方式把常量名或者变量名当做占位符加入到长字符串中
print("\(b) have \(age) years old.")

2. 分号

  • Swift 并不强制要求你在每条语句的结尾处使用分号。
  • 有一种情况下必须要用分号,即你打算在同一行内写多条独立的语句:
 let a = 10 ; print(a)

3. 整数

Swift 提供了8、16、32和64位的有符号和无符号整数类型。

3.1 整数范围

  • 访问不同整数类型的 min 和 max 属性来获取对应类型的最小值和最大值:
// minValue 为 0,是 UInt8 类型
let minValue = UInt8.min  
// maxValue 为 255,是 UInt8 类型
let maxValue = UInt8.max  

3.2 Int

Swift 提供了一个特殊的整数类型 Int,特殊的无符号类型 UInt,长度与当前平台的原生字长相同:

  • 在32位平台上,Int 和 Int32 长度相同;UInt 和 UInt32 长度相同。
  • 在64位平台上,Int 和 Int64 长度相同;UInt 和 UInt64 长度相同。

尽量不要使用 UInt,除非你真的需要存储一个和当前平台原生字长相同的无符号整数。除了这种情况,最好使用 Int,即使你要存储的值已知是非负的。统一使用 Int 可以提高代码的可复用性,避免不同类型数字之间的转换,并且匹配数字的类型推断,

3.3 浮点数

  • Double 表示64位浮点数。当需要存储很大或者高精度的浮点数时使用此类型。
  • Float 表示32位浮点数。精度要求不高的话可以使用此类型。

4. 类型安全和类型推断

  • Swift 是一个类型安全(type safe)的语言。类型安全的语言可以让你清楚地知道代码要处理的值的类型。如果你的代码需要一个 String,你绝对不可能不小心传进去一个 Int
  • Swift 会使用类型推断(type inference)来选择合适的类型。有了类型推断,编译器可以在编译代码的时候自动推断出表达式的类型。原理很简单,只要检查你赋的值即可。
let a = 10  //  被推测为 Int 类型
let b = 3.1415926   //  被推测为 Double 类型
let c = 3 + 0.1415926   //  被推测为 Double 类型

5. 数值型字面量

  • 十进制数:没有前缀
  • 二进制数:前缀是 0b
  • 八进制数:前缀是 0o
  • 十六进制数:前缀是 0x
let decimalInteger = 17
let binaryInteger = 0b10001       // 二进制的17
let octalInteger = 0o21           // 八进制的17
let hexadecimalInteger = 0x11     // 十六进制的17

十进制浮点数也可以有一个可选的指数(exponent),通过大写或者小写的 e 来指定;十六进制浮点数必须有一个指数,通过大写或者小写的 p 来指定。

  • 如果一个十进制数的指数为 exp,那这个数相当于基数和10^exp 的乘积:

1.25e2 表示 1.25 × 10^2,等于 125.0。
1.25e-2 表示 1.25 × 10^-2,等于 0.0125。

  • 如果一个十六进制数的指数为 exp,那这个数相当于基数和2^exp 的乘积:

0xFp2 表示 15 × 2^2,等于 60.0。
0xFp-2 表示 15 × 2^-2,等于 3.75。

let a= 12.1875  //  12.1875
let b= 1.21875e1    //  12.1875
let c= 0xC.3p0  //  12.1875

//  数值类字面量可以包括额外的格式来增强可读性。整数和浮点数都可以添加额外的零并且包含下划线,并不会影响字面量
let h= 000123.456
let i= 1_000_000
let j= 1_000_000.000_000_1

6. 数值型类型转换

6.1 整数转换

不同整数类型的变量和常量可以存储不同范围的数字。

  • Int8 类型: -128 --127
  • UInt8 类型: 0~255。

如果数字超出了常量或者变量可存储的范围,编译的时候会报错

let a: UInt16 = 2_000
let b: UInt8 = 1
let c= a+ UInt16(b)

6.2整数和浮点数转换

  • 整数和浮点数的转换必须显式指定类型
let a= 3
let b= 0.14159
let c = Double(a) + b   // pi 等于 3.14159,所以被推测为 Double 类型
  • 浮点数到整数的反向转换
let a= 3.14159
let b= Int(a)   // integerPi 等于 3,所以被推测为 Int 类型

7. 类型别名

类型别名(type aliases)就是给现有类型定义另一个名字。

typealias GDUInt16 = UInt16
var a = GDUInt16.min    // a 现在是 0

8. 布尔值

Swift 有两个布尔常量,truefalse

let a = true
if a {
    print("true")
} else {
    print("false")
}
// 输出“true”

9. 元组

元组(tuples)把多个值组合成一个复合值。元组内的值可以是任意类型,并不要求是相同类型。

  • http404Error 的类型是 (Int, String),值是 (404, "Not Found")
let http404Error = (404, "Not Found")
  • 可以将一个元组的内容分解(decompose)成单独的常量和变量,然后就可以正常使用它们了:
let (statusCode, statusMessage) = http404Error
print("The status code is \(statusCode)")   // 输出“The status code is 404”
print("The status message is \(statusMessage)") // 输出“The status message is Not Found”
  • 如果你只需要一部分元组值,分解的时候可以把要忽略的部分用下划线(_)标记
let (justTheStatusCode, _) = http404Error
print("The status code is \(justTheStatusCode)")    // 输出“The status code is 404”
  • 可以通过下标来访问元组中的单个元素,下标从零开始:
print("The status code is \(http404Error.0)")   // 输出“The status code is 404”
print("The status message is \(http404Error.1)")    // 输出“The status message is Not Found”
注意:
    当遇到一些相关值的简单分组时,元组是很有用的。元组不适合用来创建复杂的数据结构。如果你的数据结构比较复杂,不要使用元组,用类或结构体去建模。

9. 可选类型

可选类型(optionals)来处理值可能缺失的情况。可选类型表示两种可能: 或者有值, 或者根本没有值

  • Swift 的 Int 类型有一种构造器,作用是将一个 String 值转换成一个 Int 值。然而,并不是所有的字符串都可以转换成一个整数。字符串 "123" 可以被转换成数字 123 ,但是字符串 "hello, world" 不行。
let possibleNumber = "123"
let convertedNumber = Int(possibleNumber)   // convertedNumber 被推测为类型 "Int?", 或者类型 "optional Int"
注意:
    因为该构造器可能会失败,所以它返回一个可选类型(optional)Int,而不是一个 Int。一个可选的 Int 被写作 Int? 而不是 Int。问号暗示包含的值是可选类型,也就是说可能包含 Int 值也可能不包含值。(不能包含其他任何值比如 Bool 值或者 String 值。只能是 Int 或者什么都没有。)

9.1 nil

  • 可以给可选变量赋值为 nil 来表示它没有值:
var serverResponseCode: Int? = 404  // serverResponseCode 包含一个可选的 Int 值 404
serverResponseCode = nil    // serverResponseCode 现在不包含值
注意:
    nil 不能用于非可选的常量和变量。如果你的代码中有常量或者变量需要处理值缺失的情况,请把它们声明成对应的可选类型。
  • 如果你声明一个可选常量或者变量但是没有赋值,它们会自动被设置为 nil:
var surveyAnswer: String?   // surveyAnswer 被自动设置为 nil
注意:
    Swift 的 nil 和 Objective-C 中的 nil 并不一样。在 Objective-C 中,nil 是一个指向不存在对象的指针。在 Swift 中,nil 不是指针——它是一个确定的值,用来表示值缺失。任何类型的可选状态都可以被设置为 nil,不只是对象类型。

9.2 强制解析

当你确定可选类型确实包含值之后,你可以在可选的 名字后面加一个感叹号(!)来获取值 。这个惊叹号表示“我知道这个可选有值,请使用它。”这被称为可选值的强制解析(forced unwrapping)。

if convertedNumber != nil {
    print("convertedNumber has an integer value of \(convertedNumber!).")
}
注意
    使用 ! 来获取一个不存在的可选值会导致运行时错误。使用 ! 来强制解析值之前,一定要确定可选包含一个非 nil 的值。

9.3 可选绑定

  • 使用可选绑定(optional binding)来判断可选类型是否包含值,如果包含就把值赋给一个临时常量或者变量。
//  如果 someOptional  包含一个值,创建一个叫做 constantName 的新常量并将可选包含的值赋给它。”
if let constantName = someOptional {
    //  如果转换成功,constantName 常量可以在 if 语句的第一个分支中使用。它已经被可选类型 包含的 值初始化过,所以不需要再使用 ! 后缀来获取它的值。
}
  • 可包含多个可选绑定或多个布尔条件在一个 if 语句中,只要使用逗号分开就行。只要有任意一个可选绑定的值为 nil,或者任意一个布尔条件为 false,则整个 if 条件判断为 false,这时你就需要使用嵌套 if 条件语句来处理:
if let firstNumber = Int("4"), let secondNumber = Int("42"), firstNumber < secondNumber && secondNumber < 100 {
    print("\(firstNumber) < \(secondNumber) < 100")
}   // 输出“4 < 42 < 100”

if let firstNumber = Int("4") {
    if let secondNumber = Int("42") {
        if firstNumber < secondNumber && secondNumber < 100 {
            print("\(firstNumber) < \(secondNumber) < 100")
        }
    }
}   // 输出“4 < 42 < 100”
注意
    在 if 条件语句中使用常量和变量来创建一个可选绑定,仅在 if 语句的句中(body)中才能获取到值。相反,在 guard 语句中使用常量和变量来创建一个可选绑定,仅在 guard 语句外且在语句后才能获取到值。

9.4 隐式解析可选类型

  • 有时候在程序架构中,第一次被赋值之后,可以确定一个可选类型总会有值。在这种情况下,每次都要判断和解析可选值是非常低效的,因为可以确定它总会有值。这种类型的可选状态被定义为隐式解析可选类型(implicitly unwrapped optionals)
  • 声明:把想要用作可选的类型的后面的问号(String?)改成感叹号(String!)来声明一个隐式解析可选类型。
  • 一个隐式解析可选类型其实就是一个普通的可选类型,但是可以被当做非可选类型来使用,并不需要每次都使用解析来获取可选值。
let possibleString: String? = "An optional string."
let forcedString: String = possibleString! // 需要感叹号来获取值

let assumedString: String! = "An implicitly unwrapped optional string."
let implicitString: String = assumedString  // 不需要感叹号
注意
    如果一个变量之后可能变成 nil 的话请不要使用隐式解析可选类型。如果你需要在变量的生命周期中判断是否是 nil 的话,请使用普通可选类型。

10. 错误处理

使用 错误处理(error handling) 来应对程序执行中可能会遇到的错误条件。错误处理可以推断失败的原因,并传播至程序的其他部分。

  • 当一个函数遇到错误条件,它能报错。调用函数的地方能抛出错误消息并合理处理。
func canThrowAnError() throws {
    // 这个函数有可能抛出错误
}
  • 一个函数可以通过在声明中添加 throws 关键词来抛出错误消息。当你的函数能抛出错误消息时,你应该在表达式中前置 try 关键词。
do {
    try canThrowAnError()
    // 没有错误消息抛出
} catch {
    // 有一个错误消息抛出
}
  • 一个 do 语句创建了一个新的包含作用域,使得错误能被传播到一个或多个 catch 从句。
    这里有一个错误处理如何用来应对不同错误条件的例子。
func makeASandwich() throws {
    // ...
}

do {
    try makeASandwich()
    eatASandwich()
} catch SandwichError.outOfCleanDishes {
    washDishes()
} catch SandwichError.missingIngredients(let ingredients) {
    buyGroceries(ingredients)
}
  • 在此例中,makeASandwich()(做一个三明治)函数会抛出一个错误消息如果没有干净的盘子或者某个原料缺失。因为 makeASandwich() 抛出错误,函数调用被包裹在 try 表达式中。将函数包裹在一个 do 语句中,任何被抛出的错误会被传播到提供的 catch 从句中。
  • 如果没有错误被抛出,eatASandwich() 函数会被调用。如果一个匹配 SandwichError.outOfCleanDishes 的错误被抛出,washDishes() 函数会被调用。如果一个匹配 SandwichError.missingIngredients 的错误被抛出,buyGroceries(_:) 函数会被调用,并且使用 catch 所捕捉到的关联值 [String] 作为参数。

11. 断言和先决条件

  • 断言和先决条件是在运行时所做的检查。
  • 断言帮助你在开发阶段找到错误和不正确的假设。断言仅在调试环境运行。
  • 先决条件帮助你在生产环境中探测到存在的问题。先决条件则在调试环境和生产环境中运行。
  • 在生产环境中,断言的条件将不会进行评估。这个意味着你可以使用很多断言在你的开发阶段,但是这些断言在生产环境中不会产生任何影响。

11.1使用断言进行调试

  • 可以调用 Swift 标准库的 assert(::file:line:) 函数来写一个断言。向这个函数传入一个结果为 true 或者 false 的表达式以及一条信息,当表达式的结果为 false 的时候这条信息会被显示:
let age = -3
assert(age >= 0, "A person's age cannot be less than zero") // 因为 age < 0,所以断言会触发
  • 只有 age >= 0 为 true 时,即 age 的值非负的时候,代码才会继续执行。
  • 如果 age 的值是负数,就像代码中那样,age >= 0 为 false,断言被触发,终止应用。
  • 如果不需要断言信息,可以就像这样忽略掉:
assert(age >= 0)
  • 如果代码已经检查了条件,你可以使用 assertionFailure(_:file:line:) 函数来表明断言失败了,例如:
if age > 10 {
    print("You can ride the roller-coaster or the ferris wheel.")
} else if age > 0 {
    print("You can ride the ferris wheel.")
} else {
    assertionFailure("A person's age can't be less than zero.")
}

11.2 强制执行先决条件

当一个条件可能为假,但是继续执行代码要求条件必须为真的时候,需要使用先决条件。

例如: 使用先决条件来检查是否下标越界,或者来检查是否将一个正确的参数传给函数。

  • 你可以使用全局 precondition(::file:line:) 函数来写一个先决条件。向这个函数传入一个结果为 true 或者 false 的表达式以及一条信息,当表达式的结果为 false 的时候这条信息会被显示:
// 在一个下标的实现里...
precondition(index > 0, "Index must be greater than zero.")
  • 你可以调用 preconditionFailure(_:file:line:) 方法来表明出现了一个错误,例如,switch 进入了 default 分支,但是所有的有效值应该被任意一个其他分支(非 default 分支)处理。

      注意
          如果你使用 unchecked 模式(-Ounchecked)编译代码,先决条件将不会进行检查。编译器假设所有的先决条件总是为 true(真),他将优化你的代码。然而,fatalError(_:file:line:) 函数总是中断执行,无论你怎么进行优化设定。
    
  • 你能使用 fatalError(_:file:line:) 函数在设计原型和早期开发阶段,这个阶段只有方法的声明,但是没有具体实现,你可以在方法体中写上 fatalError("Unimplemented")作为具体实现。因为 fatalError 不会像断言和先决条件那样被优化掉,所以你可以确保当代码执行到一个没有被实现的方法时,程序会被中断。

相关标签: Swift