Swift 是 iOS 和 OS X 应用开发的一门新语言。
假设你有 C 或者 Objective-C 开发经验, Swift 的非常多内容都是你熟悉的。
Swift 的类型是在 C 和 Objective-C 的基础上提出的
- Int是整型
- Double和Float是浮点型
- Bool是布尔型
- String是字符串
- Swift 还有两个实用的集合类型,请參考集合类型。
- Array
- Dictionary
- 元组(Tuple):
- Swift 还添加了 Objective-C 中没有的类型。
- 元组能够让你创建或者传递一组数据。比方作为函数的返回值时,你能够用一个元组能够返回多个值。
- 可选(Optional)类型
- 处理值缺失的情况。
- 可选表示“那儿有一个值,而且它等于 x ”或者“那儿没有值”。可选有点像在 Objective-C 中使用nil,可是它能够用在不论什么类型上,不不过类。可选类型比
Objective-C 中的nil指针更加安全也更具表现力,它是 Swift 很多强大特性的重要组成部分。
就像 C 语言一样,Swift 使用变量来进行存储并通过变量名来关联值。
在 Swift 中,值不可变的变量有着广泛的应用,它们就是常量。并且比 C 语言的常量更强大。
在 Swift 中。假设你要处理的值不须要改变。那使用常量能够让你的代码更加安全并且更好地表达你的意图。
Swift 是一个类型安全的语言,可选就是一个非常好的样例。
Swift 能够让你清楚地知道值的类型。假设你的代码期望得到一个String。类型安全会阻止你不小心传入一个Int。
你能够在开发阶段尽早发现并修正错误。
数据类型
整数
整数就是没有小数部分的数字。比方42和-23。
整数能够是有符号(正、负、零)或者无符号(正、零)。
Swift 提供了8,16,32和64位的有符号和无符号整数类型。
这些整数类型和 C 语言的命名方式非常像,比方8位无符号整数类型是UInt8,32位有符号整数类型是Int32。
就像 Swift 的其它类型一样。整数类型採用大写命名法。
整数范围
你能够訪问不同整数类型的min和max属性来获取相应类型的最大值和最小值:
let minValue = UInt8.min // minValue 为 0,是 UInt8 类型的最小值 let maxValue = UInt8.max // maxValue 为 255。是 UInt8 类型的最大值
Int
一般来说,你不须要专门指定整数的长度。Swift 提供了一个特殊的整数类型Int,长度与当前平台的原生字长同样:
- 在32位平台上。Int和Int32长度同样。
- 在64位平台上,Int和Int64长度同样。
除非你须要特定长度的整数,一般来说使用Int就够了。这能够提高代码一致性和可复用性。即使是在32位平台上,Int能够存储的整数范围也能够达到-2147483648~2147483647。大多数时候这已经足够大了。
UInt
Swift 也提供了一个特殊的无符号类型UInt,长度与当前平台的原生字长同样:
- 在32位平台上。Int和Int32长度同样。
- 在64位平台上,Int和Int64长度同样。
注意:尽量不要使用UInt,除非你真的须要存储一个和当前平台原生字长同样的无符号整数。除了这样的情况,最好使用Int。即使你要存储的值已知是非负的。
统一使用Int能够提高代码的可复用性。避免不同类型数字之间的转换。而且匹配数字的类型猜測,请參考类型安全和类型猜測。
浮点数
浮点数是有小数部分的数字,比方3.14159,0.1和-273.15。
浮点类型比整数类型表示的范围更大,能够存储比Int类型更大或者更小的数字。Swift 提供了两种有符号浮点数类型:
- Double表示64位浮点数。当你须要存储非常大或者非常高精度的浮点数时请使用此类型。
- Float表示32位浮点数。精度要求不高的话能够使用此类型。
注意:Double准确度非常高,至少有15位数字,而Float最少仅仅有6位数字。
选择哪个类型取决于你的代码须要处理的值的范围。
布尔值
Swift 有一个主要的布尔(Boolean)类型,叫做Bool。
布尔值指逻辑上的(logical),由于它们仅仅能是真或者假。
Swift 有两个布尔常量。true和false:
let orangesAreOrange = true let turnipsAreDelicious = false
orangesAreOrange和turnipsAreDelicious的类型会被猜測为Bool,由于它们的初值是布尔字面量。
就像之前提到的Int和Double一样,假设你创建变量的时候给它们赋值true或者false。那你不须要将常量或者变量声明为Bool类型。
初始化常量或者变量的时候假设所赋的值类型已知。就能够触发类型猜測,这让 Swift 代码更加简洁而且可读性更高。
当你编写条件语句比方if语句的时候,布尔值很实用:
if turnipsAreDelicious { println("Mmm, tasty turnips!") } else { println("Eww, turnips are horrible.") } // 输出 "Eww, turnips are horrible."
条件语句,比如if,请參考控制流。
假设你在须要使用Bool类型的地方使用了非布尔值,Swift 的类型安全机制会报错。
以下的样例会报告一个编译时错误:
let i = 1 if i { // 这个样例不会通过编译,会报错 }
然而,以下的样例是合法的:
let i = 1 if i == 1 { // 这个样例会编译成功 }
i == 1的比較结果是Bool类型,所以第二个样例能够通过类型检查。
类似i == 1这种比較,请參考基本操作符。
和 Swift 中的其它类型安全的样例一样,这种方法能够避免错误并保证这块代码的意图总是清晰的。
元组
元组(tuples)把多个值组合成一个复合值。元组内的值能够使随意类型,并不要求是同样类型。
以下这个样例中,(404, "Not Found")是一个描写叙述 HTTP 状态码(HTTP status code)的元组。HTTP 状态码是当你请求网页的时候 web server返回的一个特殊值。假设你请求的网页不存在就会返回一个404 Not Found状态码。
<span style="color:#666666;">let http404Error = (404, "Not Found") // http404Error 的类型是 (Int, String)。值是 (404, "Not Found") </span>
(404, "Not Found")元组把一个Int值和一个String值组合起来表示 HTTP 状态码的两个部分:一个数字和一个人类可读的描写叙述。这个元组能够被描写叙述为“一个类型为(Int, String)的元组”。
你能够把随意顺序的类型组合成一个元组,这个元组能够包括全部类型。
仅仅要你想,你能够创建一个类型为(Int, Int, Int)或者(String, Bool)或者其它不论什么你想要的组合的元组。
你能够将一个元组的内容分解(decompose)成单独的常量和变量,然后你就能够正常使用它们了:
let (statusCode, statusMessage) = http404Error println("The status code is (statusCode)") // 输出 "The status code is 404" println("The status message is (statusMessage)") // 输出 "The status message is Not Found"
假设你仅仅须要一部分元组值,分解的时候能够把要忽略的部分用下划线(_)标记:
let (justTheStatusCode, _) = http404Error println("The status code is (justTheStatusCode)") // 输出 "The status code is 404"
此外,你还能够通过下标来訪问元组中的单个元素。下标从零開始:
println("The status code is (http404Error.0)") // 输出 "The status code is 404" println("The status message is (http404Error.1)") // 输出 "The status message is Not Found"
你能够在定义元组的时候给单个元素命名:
let http200Status = (statusCode: 200, description: "OK")
给元组中的元素命名后。你能够通过名字来获取这些元素的值:
println("The status code is (http200Status.statusCode)") // 输出 "The status code is 200" println("The status message is (http200Status.description)") // 输出 "The status message is OK"
作为函数返回值时,元组很实用。一个用来获取网页的函数可能会返回一个(Int, String)元组来描写叙述是否获取成功。和仅仅能返回一个类型的值比較起来,一个包括两个不同类型值的元组能够让函数的返回信息更实用。请參考函数參数与返回。
注意:元组在暂时组织值的时候非常实用,可是并不适合创建复杂的数据结构。假设你的数据结构并非暂时使用,请使用类或者结构体而不是元组。请參考类和结构体。
可选
使用可选(optionals)来处理值可能缺失的情况。
可选表示:
- 有值。等于 x
- 没有值
注意:C 和 Objective-C 中并没有可选这个概念。
最接近的是 Objective-C 中的一个特性。一个方法要不返回一个对象要不返回nil,nil表示“缺少一个合法的对象”。然而,这仅仅对对象起作用——对于结构体,主要的 C 类型或者枚举类型不起作用。对于这些类型,Objective-C 方法通常会返回一个特殊值(比方NSNotFound)来暗示值缺失。这样的方法如果方法的调用者知道并记得对特殊值进行推断。然而,Swift 的可选能够让你暗示随意类型的值缺失,并不须要一个特殊值。
来看一个样例。Swift 的String类型有一个叫做toInt的方法。作用是将一个String值转换成一个Int值。然而,并非全部的字符串都能够转换成一个整数。字符串"123"能够被转换成数字123。可是字符串"hello, world"不行。
以下的样例使用toInt方法来尝试将一个String转换成Int:
let possibleNumber = "123" let convertedNumber = possibleNumber.toInt() // convertedNumber 被猜測为类型 "Int?"。 或者类型 "optional Int"
由于toInt方法可能会失败。所以它返回一个可选的(optional)Int,而不是一个Int。一个可选的Int被写作Int?
而不是Int。问号暗示包括的值是可选。也就是说可能包括Int值也可能不包括值。(不能包括其它不论什么值比方Bool值或者String值。仅仅能是Int或者什么都没有。)
if 语句以及强制解析
你能够使用if语句来推断一个可选是否包括值。假设可选有值,结果是true;假设没有值。结果是false。
当你确定可选包确实含值之后,你能够在可选的名字后面加一个感叹号(!)来获取值。这个惊叹号表示“我知道这个可选有值,请使用它。”这被称为可选值的强制解析(forced unwrapping):
if convertedNumber { println("(possibleNumber) has an integer value of (convertedNumber!)") } else { println("(possibleNumber) could not be converted to an integer") } // 输出 "123 has an integer value of 123"
很多其它关于if语句的内容,请參考控制流。
注意:使用!来获取一个不存在的可选值会导致执行时错误。
使用!来强制解析值之前,一定要确定可选包括一个非nil的值。
可选绑定
使用可选绑定(optional binding)来推断可选是否包括值,假设包括就把值赋给一个暂时常量或者变量。
可选绑定能够用在if和while语句中来对可选的值进行推断并把值赋给一个常量或者变量。if和while语句。请參考控制流。
像以下这样在if语句中写一个可选绑定:
if let constantName = someOptional { statements }
你能够像上面这样使用可选绑定来重写possibleNumber这个样例:
if let actualNumber = possibleNumber.toInt() { println("(possibleNumber) has an integer value of (actualNumber)") } else { println("(possibleNumber) could not be converted to an integer") } // 输出 "123 has an integer value of 123"
这段代码能够被理解为:“假设possibleNumber.toInt返回的可选Int包括一个值,创建一个叫做actualNumber的新常量并将可选包括的值赋给它。
”
假设转换成功,actualNumber常量能够在if语句的第一个分支中使用。它已经被可选包括的值初始化过,所以不须要再使用!后缀来获取它的值。
在这个样例中。actualNumber仅仅被用来输出转换结果。
你能够在可选绑定中使用常量和变量。
假设你想在if语句的第一个分支中操作actualNumber的值,你能够改成if var actualNumber,这样可选包括的值就会被赋给一个变量而很量。
隐式解析可选
如上所述,可选暗示了常量或者变量能够“没有值”。可选能够通过if语句来推断是否有值。假设有值的话能够通过可选绑定来解析值。
有时候在程序架构中,第一次被赋值之后,能够确定一个可选总会有值。
在这样的情况下,每次都要推断和解析可选值是很低效的,由于能够确定它总会有值。
这样的类型的可选被定义为隐式解析可选(implicitly unwrapped optionals)。把想要用作可选的类型的后面的问号(String?)改成感叹号(String!)来声明一个隐式解析可选。
当可选被第一次赋值之后就能够确定之后一直有值的时候,隐式解析可选很实用。隐式解析可选主要被用在 Swift 中类的构造过程中。请參考类实例之间的循环强引用。
一个隐式解析可选事实上就是一个普通的可选,可是能够被当做非可选来使用,并不须要每次都使用解析来获取可选值。
以下的样例展示了可选String和隐式解析可选String之间的差别:
let possibleString: String? = "An optional string." println(possibleString!) // 须要惊叹号来获取值 // 输出 "An optional string." let assumedString: String! = "An implicitly unwrapped optional string." println(assumedString) // 不须要感叹号 // 输出 "An implicitly unwrapped optional string."
你能够把隐式解析可选当做一个能够自己主动解析的可选。
你要做的仅仅是声明的时候把感叹号放到类型的结尾,而不是每次取值的可选名字的结尾。
注意:假设你在隐式解析可选没有值的时候尝试取值,会触发执行时错误。
和你在没有值的普通可选后面加一个惊叹号一样。
你仍然能够把隐式解析可选当做普通可选来推断它是否包括值:
if assumedString { println(assumedString) } // 输出 "An implicitly unwrapped optional string."
你也能够在可选绑定中使用隐式解析可选来检查并解析它的值:
if let definiteString = assumedString { println(definiteString) } // 输出 "An implicitly unwrapped optional string."
注意:假设一个变量之后可能变成nil的话请不要使用隐式解析可选。假设你须要在变量的生命周期中推断是否是nil的话,请使用普通可选类型。
nil
你能够给可选变量赋值为nil来表示它没有值:
var serverResponseCode: Int?= 404 // serverResponseCode 包括一个可选的 Int 值 404 serverResponseCode = nil // serverResponseCode 如今不包括值
注意:nil不能用于非可选的常量和变量。假设你的代码中有常量或者变量须要处理值缺失的情况,请把它们声明成相应的可选类型。
假设你声明一个可选常量或者变量可是没有赋值。它们会自己主动被设置为nil:
var surveyAnswer: String? // surveyAnswer 被自己主动设置为 nil
注意:nil不是指针——它是一个确定的值,用来表示值缺失。
不论什么类型的可选都能够被设置为nil,不仅仅是对象类型。
类型安全和类型猜測
Swift 是一个类型安全(type safe )的语言。
类型安全的语言能够让你清楚地知道代码要处理的值的类型。假设你的代码须要一个String。你绝对不可能不小心传进去一个Int。
因为 Swift 是类型安全的,所以它会在编译你的代码时进行类型检查(type checks),并把不匹配的类型标记为错误。这能够让你在开发的时候尽早发现并修复错误。
当你要处理不同类型的值时,类型检查能够帮你避免错误
假设你没有显式指定类型。Swift 会使用类型猜測(type inference)来选择合适的类型。检查你赋的值。编译器在编译代码的时候自己主动猜測出表达式的类型。
由于有类型猜測。Swift 非常少须要声明类型。常量和变量尽管须要明白类型。可是大部分工作并不须要你自己来完毕。
当你声明常量或者变量并赋初值的时候类型猜測很实用。
当你在声明常量或者变量的时候赋给它们一个字面量(literal value 或 literal)就可以触发类型猜測。(字面量就是会直接出如今你代码中的值。比方42和3.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类型。
数值型字面量
整数字面量能够被写作:
- 一个十进制数,没有前缀
- 一个二进制数,前缀是0b
- 一个八进制数,前缀是0o
- 一个十六进制数。前缀是0x
以下的全部整数字面量的十进制值都是17:
let decimalInteger = 17 let binaryInteger = 0b10001 // 二进制的17 let octalInteger = 0o21 // 八进制的17 let hexadecimalInteger = 0x11 // 十六进制的17
浮点字面量能够是十进制(没有前缀)或者是十六进制(前缀是0x)。小数点两边必须有至少一个十进制数字(或者是十六进制的数字)。
浮点字面量另一个可选的指数(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。
以下的这些浮点字面量都等于十进制的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
数值型类型转换
通常来讲。即使代码中的整数常量和变量已知非负,也请使用Int类型。
总是使用默认的整数类型能够保证你的整数常量和变量能够直接被复用而且能够匹配整数类字面量的类型猜測。
仅仅有在必要的时候才使用其它整数类型。比方要处理外部的长度明白的数据或者为了优化性能、内存占用等等。使用显式指定长度的类型能够及时发现值溢出而且能够暗示正在处理特殊数据。
整数转换
不同整数类型的变量和常量能够存储不同范围的数字。Int8类型的常量或者变量能够存储的数字范围是-128~127,而UInt8类型的常量或者变量能存储的数字范围是0~255。假设数字超出了常量或者变量可存储的范围,编译的时候会报错:
let cannotBeNegative: UInt8 = -1 // UInt8 类型不能存储负数。所以会报错 let tooBig: Int8 = Int8.max + 1 // Int8 类型不能存储超过最大值的数,所以会报错
因为每中整数类型都能够存储不同范围的值,所以你必须依据不同情况选择性使用数值型类型转换。这样的选择性使用的方式,能够预防隐式转换的错误并让你的代码中的类型转换意图变得清晰。
要将一种数字类型转换成还有一种。你要用当前值来初始化一个期望类型的新数字,这个数字的类型就是你的目标类型。
在以下的样例中,常量twoThousand是UInt16类型,然而常量one是Uint8类型。它们不能直接相加,由于它们类型不同。所以要调用UInt16(one)来创建一个新的UInt16数字并用one的值来初始化,然后使用这个新数字来计算:
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内部有相应构造器的值。只是你能够扩展现有的类型来让它能够接收其它类型的值(包含自己定义类型),请參考扩展。
整数和浮点数转换
整数和浮点数的转换必须显式指定类型:
let three = 3 let pointOneFourOneFiveNine = 0.14159 let pi = Double(three) + pointOneFourOneFiveNine // pi 等于 3.14159。所以被猜測为 Double 类型
这个样例中,常量three的值被用来创建一个Double类型的值,所以加号两边的数类型同样。假设不进行转换,两者无法相加。
浮点数到整数的反向转换相同行,整数类型能够用Double或者Float类型来初始化:
let integerPi = Int(pi) // integerPi 等于 3。所以被猜測为 Int 类型
当用这样的方式来初始化一个新的整数值时,浮点值会被截断。也就是说4.75会变成4。-3.9会变成-3。
注意:结合数字类常量和变量不同于结合数字类字面量。字面量3能够直接和字面量0.14159相加,由于数字字面量本身没有明白的类型。它们的类型仅仅在编译器须要求值的时候被猜測。
类型别名
类型别名(type aliases)就是给现有类型定义还有一个名字。你能够使用typealiaskeyword来定义类型别名。
如果你正在处理特定长度的外部资源的数据:
typealias AudioSample = UInt16
定义了一个类型别名之后。你能够在不论什么使用原始名的地方使用别名:
var maxAmplitudeFound = AudioSample.min // maxAmplitudeFound 如今是 0
本例中。AudioSample被定义为UInt16的一个别名。由于它是别名,AudioSample.min实际上是UInt16.min。所以会给maxAmplitudeFound赋一个初值0。