Swift 入门(一 - 基础语法)

常量和变量

Swift 复制代码
let maximumNumberOfLoginAttempts = 10
var currentLoginAttempt = 0
var x = 0.0, y = 0.0, z = 0.0

类型注解

Swift 复制代码
var welcomeMessage: String
var red, green, blue: Double

一般来说很少需要写类型注解。如果在声明常量或变量的时候赋来了一个初始值,Swift可以推断出这个量的类型。

变量和常量的命名

可以包含任何字符,包括Unicode字符,但不能数学符号、箭头,保留的(或非法的)Unicode码位,连线与制表符。也不能以数字开头,但是可以在常量与变量名的其他地方包含数字。

Swift 复制代码
let π = 3.14159
let 你好 = "你好世界"
let 🐶🐮 = "dogcow"

一旦确定了常量或者变量的类型,就不能使用相同的名字在此进行声明,或者改变其存储的值的类型。同时,也不能讲常量和变量进行互转。

如果你需要使用与 Swift 保留关键字相同的名称作为常量或者变量名,你可以使用反引号(`)将关键字包围的方式将其作为名字使用。无论如何,你应当避免使用关键字作为常量或变量名,除非你别无选择。

Swift 复制代码
var friendlyWelcome = "Hello!"
friendlyWelcome = "Bonjour!"
// friendlyWelcome 现在是 "Bonjour!"
Swift 复制代码
let languageName = "Swift"
languageName = "Swift++"
// 这会报编译时错误 - languageName 不可改变

输出常量和变量

Swift 复制代码
print(friendlyWelcome)
// 输出"Bonjour!
print("The current value of friendlyWelcome is \(friendlyWelcome)")
// 输出"The current value of friendlyWelcome is Bonjour!"

print(_:separator:terminator:) 是一个用来输出一个或多个值到适当输出区的全局函数。如果你用 Xcode,print(_:separator:terminator:) 将会输出内容到"console"面板上。separatorterminator 参数具有默认值,因此你调用这个函数的时候可以忽略它们。默认情况下,该函数通过添加换行符来结束当前行。如果不想换行,可以传递一个空字符串给 terminator 参数--例如,print(someValue, terminator:"")

分号

Swift 并不强制要求你在每条语句的结尾处使用分号,但打算在同一行内写多条独立语句时,必须要用分号。

Swift 复制代码
let cat = "🐱"; print(cat)
// 输出"🐱"

整数

Swift 复制代码
let minValue = UInt8.min  // minValue 为 0,是 UInt8 类型
let maxValue = UInt8.max  // maxValue 为 255,是 UInt8 类型

UInt:无符号类型

浮点数

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

Double 精确度很高,至少有15位数字,而 Float 只有6位数字。选择哪个类型取决于你的代码需要处理的值的范围,在两种类型都匹配的情况下,将优先选择 Double

类型安全和类型判断

Swift 复制代码
let meaningOfLife = 42
// meaningOfLife 会被推测为 Int 类型
let pi = 3.14159
// pi 会被推测为 Double 类型
let anotherPi = 3 + 0.14159
// anotherPi 会被推测为 Double 类型

数值型字面量

Swift 复制代码
let decimalInteger = 17
let binaryInteger = 0b10001       // 二进制的17
let octalInteger = 0o21           // 八进制的17
let hexadecimalInteger = 0x11     // 十六进制的17
  • 1.25e2 表示 1.25 × 10^2,等于 125.0
  • 1.25e-2 表示 1.25 × 10^-2,等于 0.0125
  • 0xFp2 表示 15 × 2^2,等于 60.0
  • 0xFp-2 表示 15 × 2^-2,等于 3.75

下面这些浮点字面量都等于十进制的12.1875:

Swift 复制代码
let decimalDouble = 12.1875
let exponentDouble = 1.21875e1
let hexadecimalDouble = 0xC.3p0

数值类字面量可以包括额外的格式来增强可读性。整数和浮点数都可以添加额外的零并且包含下划线,并不会影响字面量:

Swift 复制代码
let paddedDouble = 000123.456
let oneMillion = 1_000_000
let justOverOneMillion = 1_000_000.000_000_1

数值型类型转换

整数转换
Swift 复制代码
let cannotBeNegative: UInt8 = -1
// UInt8 类型不能存储负数,所以会报错
let tooBig: Int8 = Int8.max + 1
// Int8 类型不能存储超过最大值的数,所以会报错
Swift 复制代码
let twoThousand: UInt16 = 2_000
let one: UInt8 = 1
let twoThousandAndOne = twoThousand + UInt16(one)

现在两个数字的类型都是 UInt16,可以进行相加。目标常量 twoThousandAndOne 的类型被推断为 UInt16,因为它是两个 UInt16 值的和。

整数和浮点数转换

必须显式制定类型:

Swift 复制代码
let three = 3
let pointOneFourOneFiveNine = 0.14159
let pi = Double(three) + pointOneFourOneFiveNine
// pi 等于 3.14159,所以被推测为 Double 类型
Swift 复制代码
let integerPi = Int(pi)
// integerPi 等于 3,所以被推测为 Int 类型

当用这种方式来初始化一个新的整数值时,浮点值会被截断。也就是说 4.75 会变成 4-3.9 会变成 -3。

结合数字类常量和变量不同于结合数字类字面量。字面量 3 可以直接和字面量 0.14159 相加,因为数字字面量本身没有明确的类型。它们的类型只在编译器需要求值的时候被推测。

类型别名

Swift 复制代码
typealias AudioSample = UInt16
var maxAmplitudeFound = AudioSample.min
// maxAmplitudeFound 现在是 0

布尔值

Swift 复制代码
let orangesAreOrange = true
let turnipsAreDelicious = false

元组

Swift 复制代码
let http404Error = (404, "Not Found")
// http404Error 的类型是 (Int, String),值是 (404, "Not Found")

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"

let http200Status = (statusCode: 200, description: "OK")
print("The status code is \(http200Status.statusCode)")
// 输出"The status code is 200"
print("The status message is \(http200Status.description)")
// 输出"The status message is OK"

如果你的数据结构比较复杂,不要使用元组,用类或结构体去建模。

可选类型

使用*可选类型(optionals)*来处理值可能缺失的情况。可选类型表示两种可能: 或者有值, 你可以解析可选类型访问这个值, 或者根本没有值。

Swift 复制代码
let possibleNumber = "123"
let convertedNumber = Int(possibleNumber)
// convertedNumber 被推测为类型 "Int?", 或者类型 "optional Int"

因为该构造器可能会失败,所以它返回一个可选类型 (optional)Int,而不是一个 Int。一个可选的 Int 被写作 Int? 而不是 Int。问号暗示包含的值是可选类型,也就是说可能包含 Int 值也可能不包含值 。(不能包含其他任何值比如 Bool 值或者 String 值。只能是 Int 或者什么都没有。)

nil

Swift 复制代码
var serverResponseCode: Int? = 404
// serverResponseCode 包含一个可选的 Int 值 404
serverResponseCode = nil
// serverResponseCode 现在不包含值

nil 不能用于非可选的常量和变量。如果你的代码中有常量或者变量需要处理值缺失的情况,请把它们声明成对应的可选类型。

Swift 复制代码
var surveyAnswer: String?
// surveyAnswer 被自动设置为 nil

if 语句以及强制解析

Swift 复制代码
if convertedNumber != nil {
    print("convertedNumber contains some integer value.")
}
// 输出"convertedNumber contains some integer value."

if convertedNumber != nil {
    print("convertedNumber has an integer value of \(convertedNumber!).")
}
// 输出"convertedNumber has an integer value of 123."

当你确定可选类型确实包含值之后,你可以在可选的名字后面加一个感叹号(!)来获取值。

可选绑定

使用可选绑定 来判断可选类型是否包含值,如果包含就把值赋给一个临时常量或者变量。可选绑定可以用在 ifwhile 语句中,这条语句不仅可以用来判断可选类型中是否有值,同时可以将可选类型中的值赋给一个常量或者变量。

Swift 复制代码
if let constantName = someOptional {
    statements
}
Swift 复制代码
if let actualNumber = Int(possibleNumber) {
    print("\'\(possibleNumber)\' has an integer value of \(actualNumber)")
} else {
    print("\'\(possibleNumber)\' could not be converted to an integer")
}
// 输出"'123' has an integer value of 123"

这段代码可以被理解为:如果 Int(possibleNumber) 返回的可选 Int 包含一个值,创建一个叫做 actualNumber 的新常量并将可选包含的值赋给它。

如果转换成功,actualNumber 常量可以在 if 语句的第一个分支中使用。它已经被可选类型 包含的 值初始化过,所以不需要再使用 ! 后缀来获取它的值。在这个例子中,actualNumber 只被用来输出转换结果。

Swift 复制代码
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 语句中使用常量和变量来创建一个可选绑定。

隐式解析可选类型

有时候在程序架构中,第一次被赋值之后,可以确定一个可选类型总会有值。在这种情况下,每次都要判断和解析可选值是非常低效的,因为可以确定它总会有值。

这种类型的可选状态被定义为隐式解析可选类型(implicitly unwrapped optionals)。把想要用作可选的类型的后面的问号(String?)改成感叹号(String!)来声明一个隐式解析可选类型。

Swift 复制代码
let possibleString: String? = "An optional string."
let forcedString: String = possibleString! // 需要感叹号来获取值

let assumedString: String! = "An implicitly unwrapped optional string."
let implicitString: String = assumedString  // 不需要感叹号

如果在隐式解析可选类型没有值的时候尝试取值,会触发运行时错误。和你在没有值的普通可选类型后面加一个惊叹号一样。

Swift 复制代码
if assumedString != nil {
    print(assumedString!)
}
// 输出"An implicitly unwrapped optional string."
Swift 复制代码
if let definiteString = assumedString {
    print(definiteString)
}
// 输出"An implicitly unwrapped optional string."

如果一个变量之后可能变成 nil 的话请不要使用隐式解析可选类型。如果需要在变量的生命周期中判断是否是 nil 的话,请使用普通可选类型。

错误处理

Swift 复制代码
do {
    try canThrowAnError()
    // 没有错误消息抛出
} catch {
    // 有一个错误消息抛出
}
Swift 复制代码
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] 作为参数。

断言和先决条件

断言和先决条件是在运行时所做的检查。可以用他们来检查在执行后续代码之前是否一个必要的条件已经被满足了。如果断言或者先决条件中的布尔条件评估的结果为 true(真),则代码像往常一样继续执行。如果布尔条件评估结果为 false(假),程序的当前状态是无效的,则代码执行结束,应用程序中止。

Swift 复制代码
let age = -3
assert(age >= 0, "A person's age cannot be less than zero")
// 因为 age < 0,所以断言会触

如果不需要断言信息,可以忽略:

Swift 复制代码
assert(age >= 0)

如果代码已经检查了条件,可以使用 assertionFailure(_:file:line:) 函数来表明断言失败了:

Swift 复制代码
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.")
}

强制执行先决条件

当一个条件可能为假,但是继续执行代码要求条件必须为真时,需要使用先决条件。例如使用先决条件来检查下标越界,或者检查是否将一个正确的参数传给函数。

可以使用全局precondition(_:_:file:line:)函数来写一个先决条件。向这个函数传入一个结果为 true 或者 false 的表达式以及一条信息,当表达式的结果为 false 的时候这条信息会被显示:

Swift 复制代码
// 在一个下标的实现里...
precondition(index > 0, "Index must be greater than zero.")
相关推荐
勇闯逆流河2 小时前
【C++】AVL详解
开发语言·c++
WebInfra2 小时前
📱开源 AI 工具驱动 iOS 自动化 、接入全新 Qwen 模型 - Midscene v0.29 发布
前端·ios·测试
一口面条一口蒜2 小时前
R语言中的获取函数与替换函数
开发语言·r语言
程序员烧烤2 小时前
【Java初学基础10】一文讲清反射
java·开发语言
闲人编程3 小时前
会议安排问题之贪心算法
python·算法·ios·贪心算法·会议问题·算法改进·codecapsule
大飞pkz3 小时前
【设计模式】状态模式
开发语言·设计模式·c#·状态模式
ajassi20003 小时前
开源 C# 快速开发(十)通讯--http客户端
开发语言·开源·c#
夜猫逐梦3 小时前
【lua】luajit 命令行使用指南
开发语言·lua
利来利往3 小时前
【奇怪的bug】lua的nil不报错
开发语言·bug·lua