你的位置:首页 > 操作系统

[操作系统]Swift基础数据类型

常量和变量

常量和变量由一个特定名称来表示,如maximumNumberOfLoginAttempt 或者 welcomeMessage。常量所指向的是一个特定类型的值,如数字10或者字符”hello”。变量的值可以根据需要不断修改,而常量的值是不能够被二次修改的。

常量和变量的声明

常量和变量在使用前都需要声明,在Swift中使用let关键词来声明一个常量,var关键词声明一个变量。如下面例子

let maximumNumberOfLoginAttempts = 10var currentLoginAttempt = 0

以上代码可以理解为:

声明一个叫maximumNumberOfLoginAttempts的值为10的常量。然后声明一个变量currentLoginAttempt初始值为0。

在这个例子中,最大的登录尝试次数10是不变的,因此声明为常量。而已经登录的尝试次数是可变的,因此定义为变量。也可以在一行中声明多个变量或常量,用,号分隔:

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

注:如果一个值在之后的代码中不会再变化,应该用let关键词将它声明为常量。变量只用来存储会更改的值。

 

类型注解

在声明常量和变量时,可以使用注解来注明该变量或常量的类型。使用:号加空格加类型名在变量或常量名之后就可以完成类型注解。下面的例子就是声明了一个变量叫welcomeMessage,注解类型为字符串String:

var welcomeMessage: String

分号 “:” 在这的作用就像是在说:…是…类型的,因此上述代码可以理解为:

声明一个叫welcomeMessage的变量,它的类型是String

这个类型注解表明welcomeMessage变量能无误地存储任何字符串类型的值,比如welcomeMessage = “hello”

注:实际编程中很少需要使用类型注解,定义常量或者变量的时候Swift已经根据初始化的值确定了类型信息。Swift几乎都可以隐式的确定变量或常量的类型。

 

分号

和其它一些编程语言不同,Swift不需要使用分号 ; 来分隔每一个语句。当然你也可以选择使用分号,或者你想在一行中书写多个语句。

let cat = ""; println(cat)// prints ""

 

整数

整数就是像42和-23这样不带分数的数字,包括有符号(正数,负数,0)和无符号(正数,0)。Swift提供了8、16、32和64位的数字形式,和C语言类似,可以使用8位的无符号整数UInt8,或者32位的整数Int32.像其他Swift类型一样,这些类型名的首字母大写

整数边界

使用min或max值来获取该类型的最大最小值,如

let minValue = UInt8.min // minValue is equal to 0, and is of type UInt8let maxValue = UInt8.max // maxValue is equal to 255, and is of type UInt8

这些值边界值区分了整数的类型(比如UInt8),所以可以像该类型的其他值一样被用在表达式中而不用考虑益处的问题。

Int类型

一般来说,编程人员在写代码时不需要选择整数的位数,Swift提供了一种额外的整数类型Int,是和当前机器环境的字长相同的整数位数

  • 在32位机器上,Int和Int32一样大小
  • 在64位机器上,Int和Int64一样大小

除非你确实需要使用特定字长的正数,尽量使用Int类型。这保证了代码的可移植性。即使在32位的平台上,Int也可以存储-2,147,483,648 到2,147,483,647范围内的值,这对大部分正数来讲已经足够了。

 

5、浮点数

浮点数就是像3.14159,0.1,-273.15这样带分数的数字。浮点数可以表达比Int范围更广(更大或更小)的数值。swift支持两种带符号浮点数类型:

  • Double类型能表示64位的有符号浮点数。当需要表的数字非常大或者精度要求非常高的时候可以使用Double类型。
  • Float类型能表示32为的有符号浮点数。当需要表达的数字不需要64位精度的时候可以使用Float类型。

    注 Double 至少有15位小数,Float至少有6位小数。合适的浮点数小数位数取决于你代码里需要处理的浮点数范围。

6、类型安全和类型推导

Swift是一种类型安全的语言。类型安全就是说在编程的时候需要弄清楚变量的类型。如果您的代码部分需要一个字符串,你不能错误地传递一个整数类型。

因为Swift是类型安全的,它会在编译的时候就检查你的代码,任何类型不匹配时都会报错。这使得编程人员能够尽快捕获并尽可能早地在开发过程中修正错误。

类型检查可以在使用不同类型的值时帮助避免错误。但是,这并不意味着你必须指定每一个常量和变量所声明的类型。如果不指定你需要的类型,Swift使用类型推导来指定出相应的类型。类型推导使编译器在编译的时候通过你提供的初始化值自动推导出特定的表达式的类型。

类型推导使Swift比起C或Objective-C只需要更少的类型声明语句。常量和变量仍然显式类型,但大部分指定其类型的工作Swift已经为你完成了。

当你声明一个常量或变量并给出初始值类型的时候,类型推导就显得特别有用。这通常是通过给所声明的常量或变量赋常值来完成的。 (常值是直接出现在源代码中的值,如下面的例子42和3.14159 。 )

例如,如果您指定42到一个新的常数变量,而不用说它是什么类型,Swift推断出你想要的常量是一个整数,因为你已经初始化它为一个整数

let meaningOfLife= 42// meaningOfLife is inferred to be of typeInt

同样,如果你不指定浮点值的类型,Swift推断出你想要创建一个Double:

let pi = 3.14159// pi is inferred to be of type Double

Swift总是选择Double(而非Float)当它需要浮点数类型时。如果你在一个表达式中把整数和浮点数相加,会推导一个Double类型:

let anotherPi= 3 + 0.14159// anotherPi is also inferred to be of typeDouble

常值3没有显示指明类型,所以Swift根据表达式中的浮点值推出输出类型Double。

 

数值量表达

整型常量可以写成:

  • 一个十进制数,不带前缀
  • 一个二进制数,用前缀0b
  • 一个八进制数,用0o前缀
  • 一个十六进制数,以0x前缀

所有如下用这些整型常量都可以来表达十进制值的17

let decimalInteger= 17let binaryInteger = 0b10001 // 17 in binary notationlet octalInteger = 0o21 // 17 in octal notationlet hexadecimalInteger = 0x11 // 17 inhexadecimal notation

浮点可以是十进制(不带前缀)或十六进制(以0x前缀),小数点的两侧必须始终有一个十进制数(或十六进制数)。他们也可以有一个可选的指数,由一个大写或小写e表示十进制浮点数表示,或大写/小写p表示十六进制浮点数

带指数exp的十进制数,实际值等于基数乘以10的exp次方,如:

  • 1.25e2表示1.25×102,或者125.0.
  • 1.25e-2表示1.25×10-2,或者0.0125.

带指数exp的十六进制数,实际值等于基部数乘以2的exp次方,如:

  • 0xFp2表示15×22,或者60.0.
  • 0xFp-2表示15×2-2,或者3.75.

所有如下这些浮点常量都表示十进制的12.1875:

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

数字值可以包含额外的格式,使它们更容易阅读。整数和浮点数都可以被额外的零填充,并且可以包含下划线,以增加可读性。以上格式都不影响变量的值:

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

数值类型转换

为代码中所有通用的数值型整型常量和变量使用Int类型,即使它们已知是非负的。这意味着代码中的数值常量和变量能够相互兼容并且能够与自动推导出的类型相互匹配。

只有因为某些原因(性能,内存占用或者其他必须的优化)确实需要使用其他数值类型的时候,才应该使用这些数值类型。这些情况下使用显式指定长度的类型有助于发现值范围溢出,同时应该留下文档。

 整数转换

可以存储在一个整数常量或变量的范围根据每个数值类型是不同的。一个Int8常量或变量可以存储范围-128到127之间的数,而一个UInt8常量或变量可以存储0到255之间的数字。错误的赋值会让编译器报错:

let cannotBeNegative: UInt8 = -1// UInt8 cannot store negative numbers, and so this will report an errorlet tooBig: Int8 = Int8.max + 1// Int8 cannot store a number larger thanits maximum value,// and so this will also report an error

因为每个数字类型可以存储不同范围的值,你必须在基础数值类型上逐步做转换。这种可以防止隐藏的转换错误,并帮助明确你的代码中类型转换的意图。

要转换一个特定的数字类型到另一个,你需要定义一个所需类型的新变量,并用当前值初始化它。在下面的例子中,常量twoThousand是UInt16类型的,而常量one是UINT8类型的。它们不能被直接相加的,因为类型不同。相反的,该​​示例调用UInt16(one)来创建一个用变量one的值初始化的UInt16类型的新变量,并且使用这个值来代替原来的值参与运算:

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

可以由于相加双方的类型都是UInt16的,现在可以做加法运算了。输出常量(twoThousandAndOne)被推断为UInt16类型的,因为它是两个UInt16的值的总和。

SomeType(ofInitialValue)是Swift默认的类型转换方式。实现上看,UInt16的有一个接受UINT8值的构造器,这个构造器用于从现有UInt8构造出一个新的UInt16的变量。你不能传入任意类型的参数,它必须是一个类型的UInt16初始化能接受的类型。如何扩展现有类型,规定接受新的类型(包括你自己的类型定义)可以参见 Extensions。

整数和浮点数转换

整数和浮点类型之间的转化必须显式声明

let three = 3let pointOneFourOneFiveNine= 0.14159let pi = Double(three) +pointOneFourOneFiveNine// pi equals 3.14159, and is inferred to beof typde Double

这里,常量three的值被用来创建Double类型的新变量,从而使表达式两侧是相同的类型。如果没有这个转换,加法操作不会被允许。反之亦然,一个整数类型可以用double或float值进行初始化:

let integerPi= Int(pi)// integerPi equals 3, and is inferred tobe of type Int

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

注:数值类型常量/变量的类型转换规则和数字类型常值的转换规则不同。常值3可以直接与常值0.14159相加,因为常值没有一个明确的类型。他们的类型是被编译器推导出来的。

类型别名

类型别名为现有类型定义的可替代名称。你可以使用typealias关键字定义类型别名。类型别名可以帮助你使用更符合上下文语境的名字来指代一个已存在的类型,比如处理一个外来的有指定长度的类型的时候:

typealias AudioSample = UInt16

一旦你定义了一个类型别名,你可以在任何可能使用原来的名称地方使用别名:

var maxAmplitudeFound= AudioSample.min// maxAmplitudeFound is now 0

这里,AudioSample被定义为一个UInt16的别名。因为它是一个别名,调用AudioSample.min实际上是调用UInt16.min,从而给maxAmplitudeFound变量赋初始值0。

布尔类型

Swift中的布尔类型使用Bool定义,也被称为Logical(逻辑)类型,可选值是true和false:

let orangesAreOrange = truelet turnipsAreDelicious = false

这里 orangesAreOrange和turnipsAreDelicious的类型被推导为Bool 因为他们被初始化被Bool类型的常值。跟Int和Double类型一样,在定义布尔类型的时候不需要显式的给出数据类型,只需要直接赋值为true或false即可 。当使用确定类型的常值初始化一个常量/变量的时候,类型推导使Swift代码更精确和可读。 布尔类型在条件语句中特别适用,比如在if语句中

if turnipsAreDelicious {println("Mmm, tasty turnips!")} else {println("Eww, turnips are horrible.")}// prints "Eww, turnips are horrible."

像if语句这样的条件语句,我们会在之后的章节ControlFlow有详细介绍。 Swift的类型安全策略会防止其他非布尔类型转换为布尔类型使用,比如

let i = 1if i {// this example will not compile, and will report an error}

就会报错,但这在其他编程语言中是可行的。但是如下的定义是正确的:

let i = 1if i == 1 {// this example will compile successfully}

i == 1的结果就是一个布尔类型,所以可以通过类型检查。像i==1这种比较将会在章节[Basic Operators]中讨论。上面的例子也是一个Swift类型安全的例子。类型安全避免了偶然的类型错误,保证了代码的意图是明确的。

元组类型

元组类型可以将一些不同的数据类型组装成一个元素,这些数据类型可以是任意类型,并且不需要是同样的类型。

在下面的例子中,(404, “Not Found”) 是一个HTTP状态码。HTTP状态码是请求网页的时候返回的一种特定的状态编码。404错误的具体含义是页面未找到。

let http404Error = (404, “Not Found”) // http404Error is of type (Int, String), and equals (404, “NotFound”)

这个元组由一个Int和一个字符串String组成,这样的组合即包含了数字,也包含了便于人们认知的字符串描述。这个元组可以描述为类型(Int,String)的元组。

编程人员可以随意地创建自己需要的元组类型,比如 (Int, Int, Int), 或者(String, Bool)等。同时组成元组的类型数量也是不限的。 可以通过如下方式分别访问一个元组的值:

let (statusCode, statusMessage) = http404Errorprint("The status code is \(statusCode)")// prints "The status code is 404"print("The status message is \(statusMessage)")// prints "The status message is Not Found"

另外,也可以使用元素序号来选择元组中的值,注意序号是从0开始的

print("The status code is \(http404Error.0)")// prints "The status code is 404"print("The status message is \(http404Error.1)")// prints "The status message is Not Found"

在创建一个元组的时候,也可以直接指定每个元素的名称,然后直接使用元组名.元素名访问,如:

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

元组类型在作为函数返回值的时候特别适用,可以为函数返回更多的用户需要的信息。比如一个请求web页面的函数可以返回(Int,String)类型的元组来表征页面获取的成功或者失败。返回两个不同类型组成的元组可以比只返回一个类型的一个值提供更多的返回信息。详见Functions with Multiple Return Values

可选类型

在一个值可能不存在的时候,可以使用可选类型。这种类型的定义是:要么存在这个值,且等于x,要么在这个值 不存在。

注:这种类型在C和Objective-C中是不存在的,但是Objective-C中有一个相似的类型,叫nil,但是仅仅对对象有用。对其他的情况,Object-C方法返回一个特殊值(比如NSNotFound)来表明这个值不存在。这种方式假设方法调用者知道这个特殊值的存在和含义。Swift的可选类型帮助你定义任意的值不存在的情况。

下面给出一个例子,在Swift中String类型有一个叫toInt的方法,能够将一个字符串转换为一个Int类型。但是需要注意的是,不是所有的字符串都可以转换为整数。比如字符串”123″可以转换为123,但是”hello, world”就不能被转换。

let possibleNumber = "123"let convertedNumber = possibleNumber.toInt()// convertedNumber is inferred to be of type "Int?", or "optional Int"

由于toInt方法可能会失败,因此它会返回一个可选的Int类型,而不同于Int类型。一个可选的Int类型被记为Int?,不是Int。问号表明它的值是可选的,可能返回的是一个Int,或者返回的值不存在。

if语句和强制解包

编程人员可以使用if语句来检测一个可选类型是否包含一个特定的值,如果一个可选类型确实包含一个值,在if语句中它将返回true,否则返回false。如果你已经检测确认该值存在,那么可以使用或者输出它,在输出的时候只需要在名称后面加上感叹号(!)即可,意思是告诉编译器:我已经检测好这个值了,可以使用它了。如:

if convertedNumber {print("\(possibleNumber) has an integer value of \(convertedNumber!)")} else {print("\(possibleNumber) could not be converted to an integer")}// prints "123 has an integer value of 123"

像if语句这样的条件语句,我们会在之后的章节ControlFlow有详细介绍。

选择绑定

选择绑定帮助确定一个可选值是不是包含了一个值,如果包含,把该值转化成一个临时常量或者变量。选择绑定可以用在if或while语句中,用来在可选类型外部检查是否有值并提取可能的值。if和while语句详见ControlFlow。

方法如下:

if let constantName = someOptional {statements}

那么上一个例子也可以改写为:

var myString:String?myString = "Hello, Swift!"if let yourString = myString {  print("Your string has - \(yourString)")}else{  print("Your string does not have a value")}

 

隐式解包可选类型

在上面的例子中,可选类型表示一个常量/变量可以没有值。可选类型可以被if语句检测是否有值,并且可以被可选绑定解包。

但是在一些情况下,可选类型是一直有效的,那么可以通过定义来隐式地去掉类型检查,强制使用可选类型。这些可选类型被成为隐式解包的可选类型。你可以直接在类型后面加! 而不是?来指定。

隐式解包的可选类型主要用在一个变量/常量在定义瞬间完成之后值一定会存在的情况。这主要用在类的初始化过程中,详见Unowned References and Implicitly Unwrapped Optional Properties.

隐式解包的可选类型本质是可选类型,但是可以被当成一般类型来使用,不需要每次验证值是否存在。如下的例子展示了可选类型和解包可选类型之间的区别。

 

let possibleString: String? = "An optional string."print(possibleString!) // requires an exclamation mark to access its value// prints "An optional string."let assumedString: String! = "An implicitly unwrapped optional string."print(assumedString) // no exclamation mark is needed to access its value// prints "An implicitly unwrapped optional string."

你可以把隐式解包可选类型当成对每次使用的时候自动解包的可选类型。即不是每次使用的时候在变量/常量后面加!而是直接在定义的时候加。

注:如果一个隐式解包的可选类型不包含一个实际值,那么对它的访问会抛出一个运行时错误。在变量/常量名后面加!的情况也是一样的。

你依然可以把解包可选类型当成正常的可选类型来探测是否有值。

if assumedString {print(assumedString)}// prints "An implicitly unwrapped optional string."

或者通过选择绑定检查

if let definiteString = assumedString {print(definiteString)}// prints "An implicitly unwrapped optional string."

注:如果一个可选类型存在没有值的可能的话,不应该使用解包可选类型。这种情况下,一定要使用正常的可选类型。

断言

可选类型让编程人员可以在运行期检测一个值是否存在,然后使用代码来处理不存在的情况。但是有些情况下,如果一个值 不存在或者值不满足条件会直接影响代码的执行,这个时候就需要使用断言。这种情况下,断言结束程序的执行从而提供调试的依据。

使用断言调试

断言是一种实时检测条件是否为true的方法,也就是说,断言假定条件为true。断言保证了后续代码的执行依赖于条件的成立。如果条件满足,那么代码继续执行,如果这个条件为false,那么代码将会中断执行。

在Xcode中,在调试的时候如果中断,可以通过查看调试语句来查看断言失败时的程序状态。断言也能提供适合的debug信息。 使用全局函数assert来使用断言调试,assert函数接受一个布尔表达式和一个断言失败时显示的消息,如:

let age = -3assert(age >= 0, "A person's age cannot be less than zero")// this causes the assertion to trigger, because age is not >= 0

当前一个条件返回false的时候,后面的错误日志将会输出。

在这个例子中,只有当age >= 0的时候,条件被判定为true,但是age = -3,所以条件判定为false,输出错误日志 “A person’s age cannot be less than zero”。

当然错误日志也可以省略,但是这样不利于调试,如

assert(age >= 0)

使用断言的时机

当需要检测一个条件可能是false,但是代码运行必须返回true的时候使用。下面给出了一些常用场景,可能会用到断言检测:

  • 传递一个整数类型下标的时候,比如作为数组的Index,这个值可能太小或者太大,从而造成数组越界;
  • 传递给函数的参数,但是一个无效的参数将不能在该函数中执行
  • 一个可选类型现在是nil,但是在接下来的代码中,需要是非nil的值才能够继续运行。

详见 Subscripts和Functions

注:断言会导致程序运行的中止,所以如果异常是预期可能发生的,那么断言是不合适的。这种情况下,异常是更合适的。断言保证错误在开发过程中会被发现,发布的应用里最好不要使用。