Swift6.0基础知识 -- 3、基本数据类型1

目录

    • 1、常量和变量
      • 1.1、声明常量和变量
      • 1.2、类型注解
      • 1.3、命名常量和变量
      • 1.4、打印常量和变量
    • 2、注释
    • 3、分号
    • 4、整数
      • 4.1、整数边界
      • 4.2、Int(整数)
      • 4.3、UInt(无符号整数)
    • 5、浮点数
    • 6、类型安全和类型推断
    • 7、数值字面量
    • 8、数字类型转换
      • 8.1、整数转换
      • 8.2、整数和浮点数转换
    • 9、类型别名
    • 10、布尔类型

Swift 提供了许多基本数据类型,包括表示整数的 Int、表示浮点数的 Double、表示布尔值的 Bool 和表示文本的 String。Swift 还提供了三种主要集合类型( Array(数组), Set(集合),和 Dictionary(字典))的强大版本,详见 doc:CollectionTypes。

Swift 使用变量来存储值,并通过标识名称来引用值。Swift 还广泛使用不可更改其值的变量。这些变量被称为常量,在整个 Swift 中都有使用,以便在处理无需更改的值时使代码更安全、更清晰。

除了熟悉的类型外,Swift 还引入了元组等高级类型。通过元组,你可以创建并传递一组值。你可以使用元组从函数返回一个包含了多个值的复合值。

Swift 还引入了可选类型,用于处理值缺失的情况。可选类型要么表示“变量_有_值,且等于 x”,要么表示“压根就_没有_值”。

Swift 是一种_类型安全_的语言,这意味着该语言可以帮助你明确代码可以处理的值的类型。如果你的部分代码需要字符串,类型安全可以防止你错误地将整数传递给它。同样,类型安全也能防止你不小心将可选字符串传递给需要非可选字符串的代码。类型安全可帮助你在开发过程中尽早发现并修复错误。

1、常量和变量

常量和变量将名称(如 maximumNumberOfLoginAttemptswelcomeMessage)与特定类型的值(如数字 10 或字符串 "Hello")相关联。_常量_的值一旦设置就不能更改,而_变量_则可以在将来设置不同的值。

1.1、声明常量和变量

常量和变量在使用前必须先声明。使用 let 关键字声明常量,使用 var 关键字声明变量。下面举例说明如何使用常量和变量来追踪用户尝试登录的次数:

let maximumNumberOfLoginAttempts = 10
var currentLoginAttempt = 0

该代码可理解为:

“声明一个名为 maximumNumberOfLoginAttempts 的新常量,并赋予其 10 的值。然后,声明一个名为 currentLoginAttempt 的新变量,并赋予其 0 的初始值。”

在这个示例中,允许的最大登录尝试次数被声明为一个常量,因为最大值永远不会改变。当前登录尝试计数器被声明为变量,因为每次登录尝试失败后,该值都必须递增。

如果代码中的某个存储值不会改变,请使用 let 关键字将其声明为常量。变量只用于存储会发生变化的值。

在声明常量或变量时,可以像上面的示例一样,在声明中为其赋值。或者你也可以稍后在程序中为变量分配初始值,只要能保证在第一次读取前它有值即可。

var environment = "development"
let maximumNumberOfLoginAttempts: Int
// maximumNumberOfLoginAttempts 尚无值。if environment == "development" {maximumNumberOfLoginAttempts = 100
} else {maximumNumberOfLoginAttempts = 10
}
// 现在 maximumNumberOfLoginAttempts 有了值,可以读取了。

在本例中,登录尝试的最大次数是常数,其值取决于环境。在开发环境中,其值为 100;在其他环境中,其值为 10。if 语句的两个分支都将 maximumNumberOfLoginAttempts 初始化为某个值,从而保证该常量一定有一个值。有关 Swift 如何在以这种方式设置初始值时检查代码的详情,请参阅 doc:Declarations#Constant-Declaration。

你可以在一行中声明多个常量或多个变量,中间用逗号隔开:

var x = 0.0, y = 0.0, z = 0.0

1.2、类型注解

在声明常量或变量时,可以提供_类型注解_,以明确常量或变量可以存储的值的类型。编写类型注解时,在常量或变量名后加上冒号,后跟一个空格,然后是要使用的类型名称。

本例为名为 welcomeMessage 的变量提供了一个类型注解,以指示该变量可以存储字符串值:

var welcomeMessage: String

声明中的冒号表示“…类型的…”,因此上面的代码可以理解为:

“声明一个名为 welcomeMessage 的字符串类型的变量。”

短语“字符串类型”意味着“可以存储任何字符串值“。请将其理解为变量可以存储的“值的类型”。

现在,你可以将 welcomeMessage 变量设置为任何字符串值且不会出错:

welcomeMessage = "Hello"

你可以在一行中定义多个相同类型的相关变量,中间用逗号隔开,并在最后一个变量名后加一个类型注解:

var red, green, blue: Double

备注: 在实践中很少需要编写类型注解。如果你在定义常量或变量时为其提供了初始值,Swift 几乎总是可以推断出该常量或变量的类型,如 doc:TheBasics#类型安全和类型推断 中所述。上面的 welcomeMessage 示例中,没有提供初始值,因此 welcomeMessage 变量的类型是通过类型注解指定的,而不是从初始值推断出来的。

1.3、命名常量和变量

常量和变量名几乎可以包含任何字符,包括 Unicode 字符:

let π = 3.14159
let 你好 = "你好世界"
let 🐶🐮 = "dogcow"

常量和变量名不能包含空格字符、数学符号、箭头、专用的 Unicode 标量值,或画线和画框字符。常量和变量名也不能以数字开头,但数字可以包含在名称的其他部分。

一旦声明了某个类型的常量或变量,就不能再用相同的名称声明,也不能更改它以存储不同类型的值。同时也不能将常量改为变量,或将变量改为常量。

备注: 如果要使用与 Swift 保留关键字相同的名称命名常量或变量,在使用关键字时,请用反引号 (```) 包裹。不过,除非别无他选,请尽量避免使用关键字作为变量名称。

你可以将现有变量的值更改为另一个兼容类型的值。在本例中,friendlyWelcome 的值从 "Hello!" 改为了 "Bonjour!"

var friendlyWelcome = "Hello!"
friendlyWelcome = "Bonjour!"
// friendlyWelcome 的当前值是 "Bonjour!"

与变量不同,常量的值在设置后不能更改。在编译代码时,如果试图更改常量的值,就会报错:

let languageName = "Swift"
languageName = "Swift++"
// 这是一个编译时错误:languageName 不能更改。

1.4、打印常量和变量

使用 print(_:separator:terminator:) 函数可以打印常量或变量的当前值:

print(friendlyWelcome)
// 打印 "Bonjour!"

print(_:separator:terminator:) 函数是一个全局函数,用于将一个或多个值打印到适当的输出端。例如,在 Xcode 中,print(_:separator:termininator:) 函数将在 Xcode 的 “控制台” 窗格中打印输出。参数 separator(分隔符) 和 terminator(结束符) 有默认值,因此在调用此函数时可以省略。默认情况下,该函数通过添加换行符来结束打印行。如果要打印一个值且不换行,可以传递一个空字符串作为结束符,例如 print(someValue,termininator:"")。有关带默认值参数的信息,请参阅 doc:Functions#Default-Parameter-Values。

Swift 使用_字符串插值_将常量或变量的名称作为占位符包含在较长的字符串中,并提示 Swift 将其替换为该常量或变量的当前值。将名称包在括号中,并在左括号前用反斜杠进行转义:

print("The current value of friendlyWelcome is \(friendlyWelcome)")
// 打印 "The current value of friendlyWelcome is Bonjour!"

备注: 在 doc:StringsAndCharacters#String-Interpolation 中描述了所有字符串插值选项。

2、注释

使用注释在代码中包含不可执行的文本,作为给自己的注释或提醒。在编译代码时,Swift 编译器会忽略注释。

Swift 中的注释与 C 语言中的注释非常相似。单行注释以两个正斜杠 (//) 开头:

// 这是一个注释。

多行注释以正斜杠 + 星号 (/*) 开始,并以星号 + 正斜杠 (*/) 结束:

/* 这也是一个注释
但写了多行。*/

与 C 语言中的多行注释不同,Swift 中的多行注释可以嵌套在其他多行注释中。在编写嵌套注释时,你可以先编写一个多行注释块,然后在第一个注释块中编写第二个多行注释块。然后关闭第二个注释块,接着关闭第一个注释块:

/* 这是第一个多行注释的开始。/* 这是第二个嵌套的多行注释。*/
这是第一个多行注释的结束。*/

通过嵌套多行注释,即使代码中已包含多行注释,你也能快速、轻松地注释大块代码。

3、分号

与许多其他语言不同,Swift 并不要求你在代码中的每条语句后都写上分号(;),不过如果你愿意,也可以这样做。不过,如果你想在一行中编写多个独立语句,则_必须_使用分号:

let cat = "🐱"; print(cat)
// 打印 "🐱"

4、整数

整数_是没有小数成分的数字,如 42-23。 整数可以是_有符号的(正数、零或负数),也可以是_无符号的_(正数或零)。

Swift 提供 8、16、32 和 64 位有符号和无符号整数。这些整数遵循与 C 类似的命名规则,即 8 位无符号整数的类型是 UInt8,32 位有符号整数的类型是 Int32。与 Swift 中的所有类型一样,这些整数类型的名称也是大写的。

4.1、整数边界

你可以使用 minmax 属性访问每个整数类型的最小值和最大值:

let minValue = UInt8.min  // minValue 等于 0,类型为 UInt8
let maxValue = UInt8.max  // maxValue 等于 255,类型为 UInt8

这些属性的值属于适当大小的数字类型(如上例中的 UInt8),因此可以在表达式中与同类型的其他值一起使用。

4.2、Int(整数)

在大多数情况下,你不需要在代码中使用特定大小的整数。Swift 提供了一种额外的整数类型 Int,其大小与当前平台的原生字长大小相同:

  • 在 32 位平台上,Int 的大小与 Int32 相同。
  • 在 64 位平台上,Int 的大小与 Int64 相同。

除非你需要使用特定大小的整数,否则在代码中始终使用 Int 表示整数值。这有助于代码的一致性和互操作性。即使在 32 位平台上,Int 也可以存储介于 -2,147,483,6482,147,483,647 之间的任何值,其大小足以容纳许多整数范围。

4.3、UInt(无符号整数)

Swift 还提供了无符号整数类型 UInt,其大小与当前平台的原生字长大小相同:

  • 在 32 位平台上,UInt 的大小与 UInt32 相同。
  • 在 64 位平台上,UInt 的大小与 UInt64 相同。

备注: 只有在特别需要无符号整数类型且其大小与平台的原生字长大小相同时,才使用 UInt。如果不是这种情况,最好使用 Int,即使要存储的值是非负值。对整数值一致使用 Int 可以提高代码的互操作性,避免在不同数字类型之间进行转换,并符合 doc:TheBasics#类型安全和类型推断 中所述的整数类型推断。

5、浮点数

_浮点数_是带有小数成分的数字,如 3.141590.1-273.15

与整数类型相比,浮点类型可以表示的数值范围更广,而且可以存储比 Int 类型大得多或小得多的数字。Swift 提供了两种带符号浮点数类型:

  • Double 表示 64 位浮点数。
  • Float 表示 32 位浮点数。

备注: Double 的精度至少为小数点后 15 位,而 Float 的精度可以少至小数点后 6 位。使用哪种浮点类型更合适,取决于代码中需要处理数值的性质和范围。在两种类型都适用的情况下,Double 是首选。

6、类型安全和类型推断

Swift 是一种_类型安全_的语言。类型安全语言鼓励你显式指示代码可处理的值的类型。如果代码的一部分需要 String,你就不能错误地将 Int 传递给它。

由于 Swift 是类型安全的语言,因此它在编译代码时会执行_类型检查_,并将任何不匹配的类型标记为错误。这样,你就能在开发过程中尽早发现并修复错误。

类型检查可帮助你在处理不同类型的值时避免错误。但是,这并不意味着你必须指定你声明的每个常量和变量的类型。如果你没有指定所需值的类型,Swift 会使用_类型推断_来确定适当的类型。类型推断使编译器在编译代码时,仅通过检查你提供的值,就能自动推断出特定表达式的类型。

由于有了类型推断,Swift 所需的类型声明比 C 或 Objective-C 等语言要少得多。常量和变量仍然是显式类型的,但为其指定类型的大部分工作都是自动完成的。

当你声明一个带有初始值的常量或变量时,类型推断尤其有用。通常的做法是在声明常量或变量时为其赋_字面量_。(字面量指的是直接出现在源代码中的值,如下面示例中的 423.14159)。

例如,如果你将字面量 42 赋给一个新常量,但没有说明它是什么类型,Swift 就会推断你希望该常量是一个 Int 常量,因为你刚使用了一个看起来像整数的数字对它进行了初始化:

let meaningOfLife = 42
// meaningOfLife 被推断为 Int 类型

同样,如果你没有为浮点数字面量指定类型,Swift 会认为你想创建一个 Double

let pi = 3.14159
// pi 推断为 Double 类型

在推断浮点数类型时,Swift 总是选择 Double(而非 Float)。

如果在表达式中结合使用整数和浮点数字面量,则会根据上下文推断出 Double 类型:

let anotherPi = 3 + 0.14159
// anotherPi 也被推断为 Double 类型

3 的字面量本身并没有显式类型,因此可以根据加法中的浮点数字面量推断出合适的输出类型 Double

7、数值字面量

整数字面量可以写成:

  • 不带前缀的_十进制_数
  • 0b 前缀的_二进制_数
  • 带前缀 0o 的_八进制_数
  • 带前缀 0x 的_十六进制_数

以下所有整数字面量的十进制值都是 17

let decimalInteger = 17
let binaryInteger = 0b10001       // 以二进制表示的 17
let octalInteger = 0o21           // 以八进制表示的 17
let hexadecimalInteger = 0x11     // 以十六进制表示的 17

浮点字面量可以是十进制(不带前缀),也可以是十六进制(带 0x 前缀)。浮点数的小数点两边必须始终有一个数字(或十六进制数)。十进制浮点数还可以有一个可选的_指数_,用大写或小写 e 表示;十六进制浮点数必须有一个指数,用大写或小写 p 表示。

对于指数为 x 的十进制数,字面量是基数乘以 10ˣ:

  • 1.25e2 表示 1.25 x 10² 或 125.0
  • 1.25e-2 表示 1.25 x 10⁻² 或 0.0125

对于指数为 x 的十六进制数,字面量是基数乘以 2ˣ:

  • 0xFp2 表示 15 x 2² 或 60.0
  • 0xFp-2 表示 15 x 2⁻²,或 3.75

以下所有浮点字面量的十进制值都是 12.1875

let decimalDouble = 12.1875
let exponentDouble = 1.21875e1
let hexadecimalDouble = 0xC.3p0

数值字面量可以包含额外的格式,以便于阅读。整数和浮点数都可以填充额外的零,也可以包含下划线,以提高可读性。这两种格式都不会影响字面量的实际值:

let paddedDouble = 000123.456
let oneMillion = 1_000_000
let justOverOneMillion = 1_000_000.000_000_1

8、数字类型转换

对代码中的所有通用整数常量和变量使用 Int 类型,即使它们已知是非负值。在通常情况下使用默认整数类型意味着整数常量和变量可以立即在代码中互操作,并且与整数字面量的推断类型相匹配。

只有当手头的任务特别需要其他整数类型时,或者因为外部数据源提供了显式大小的数据,或者为了性能、内存使用或其他必要的优化,再考虑使用其他整数类型。在这些情况下使用显式大小的类型有助于捕捉任何意外的数值溢出,并隐式地记录所使用数据的性质。

8.1、整数转换

每种数字类型的整数常量或变量可存储的数字范围都不同。Int8 常量或变量可以存储 -128127 之间的数字,而 UInt8 常量或变量可以存储 0255 之间的数字。编译代码时,如果一个数字无法放入一个规定大小的整数类型常量或变量中,就会报错:

let cannotBeNegative: UInt8 = -1
// UInt8 不能存储负数,因此会报错
let tooBig: Int8 = Int8.max + 1
// Int8 不能存储大于其最大值的数字,
// 因此也会报错

由于每种数值类型可以存储不同范围的值,因此必须根据具体情况选择是否进行数值类型转换。这种选择加入的方法可以防止隐藏的转换错误,并有助于在代码中明确类型转换的意图。

要将一种特定的数字类型转换为另一种,需要用现有值初始化一个所需类型的新数字。在下面的示例中,常量 twoThousand 的类型是 UInt16,而常量 one 的类型是 UInt8。由于它们的类型不同,因此不能直接相加。所以,本例调用 UInt16(one) 创建了一个初始值为 one 的新 UInt16,并用这个值代替原来的值进行计算:

let twoThousand: UInt16 = 2_000
let one: UInt8 = 1
let twoThousandAndOne = twoThousand + UInt16(one)

因为加法的两边现在都是 UInt16 类型,所以加法是允许的。输出常量 (twoThousandAndOne) 被推断为 UInt16 类型,因为它是两个 UInt16 值的和。

SomeType(ofInitialValue) 是调用 Swift 类型初始化器并赋予初始值的默认方式。在后台,UInt16 有一个接受 UInt8 值的初始化器,因此该初始化器用于从现有的 UInt8 生成一个新的 UInt16。但在这里不能使用_任意_类型,而必须是 UInt16 提供了初始化器的类型。扩展现有类型以提供接受新类型(包括你自己的类型定义)的初始化器,将在 doc:Extensions 中介绍。

8.2、整数和浮点数转换

整数和浮点数值类型之间必须进行显式转换:

let three = 3
let pointOneFourOneFiveNine = 0.14159
let pi = Double(three) + pointOneFourOneFiveNine
// pi 等于 3.14159,并被推断为 Double 类型

在这里,常量 three 的值被用来创建一个 Double 类型的新值,这样加法的两边都是相同的类型。如果没有这种转换,加法运算将无法进行。

浮点数到整数也必须进行显式转换。整数类型可以用 DoubleFloat 值进行初始化:

let integerPi = Int(pi)
// integerPi 等于 3,并被推断为 Int 类型

用这种方法初始化一个新的整数值时,浮点数总是被截断的。这意味着 4.75 变为 4-3.9 变为 -3

备注: 组合数值常量和变量的规则与组合数值字面量的规则不同。字面量 3 可以与字面量 0.14159 相加,因为数值字面量本身并没有显式类型。只有在编译器对其进行评估时才会推断出其类型。

9、类型别名

_类型别名_用于定义现有类型的替代名称。你可以使用 typealias 关键字定义类型别名。

当你想用一个更适合上下文的名称来代指现有类型时,比如在处理源自外部的特定大小的数据时,类型别名就非常有用:

typealias AudioSample = UInt16

一旦定义了类型别名,就可以在任何可以使用原名的地方使用此别名:

var maxAmplitudeFound = AudioSample.min
// maxAmplitudeFound 现在为 0

在这里,AudioSample 被定义为 UInt16 的别名。因为是别名,所以调用 AudioSample.min 实际上是调用 UInt16.min,为 maxAmplitudeFound 变量提供一个初始值 0

10、布尔类型

Swift 有一种基本的_布尔_类型,称为 Bool。布尔值又被称为_逻辑值_,因为它们只能为真或假。Swift 提供了两个布尔常量值:truefalse

let orangesAreOrange = true
let turnipsAreDelicious = false

orangesAreOrangeturnipsAreDelicious 的类型已被推断为 Bool,因为它们使用了布尔字面量初始化。与上述的 IntDouble 一样,如果你在创建常量或变量时将其设置为 truefalse,则无需将其声明为 Bool。类型推断有助于让 Swift 代码在使用类型已知的值初始化常量或变量时更简洁易读。

布尔值在使用条件语句(如 if 语句)时尤其有用:

if turnipsAreDelicious {print("Mmm, tasty turnips!")
} else {print("Eww, turnips are horrible.")
}
// 打印 "Eww, turnips are horrible."

doc:ControlFlow 将详细介绍 if 语句等条件语句。

Swift 的类型安全防止非布尔值被替换为布尔值。下面的示例代码会报告编译时错误:

let i = 1
if i {// 该示例将无法编译,并报错
}

不过,下面的替代示例是合法的:

let i = 1
if i == 1 {// 此示例将成功编译
}

i == 1 的比较结果是 Bool 类型,因此第二个示例通过了类型检查。类似 i == 1 的比较结果将在 doc:BasicOperators 中讨论。

与 Swift 中其他类型安全示例一样,这种方法可以避免意外错误,并确保特定代码部分的意图始终清晰明了。