起舞弄影掀闺帘*/
//-------------------------------------------------------------------------------------------------------------------------------------
虽然Swift是一个为开发iOS和OS X app设计的全新编程语言,但是Swift的很多特性还是跟C和Objective-C相似。
Swift也提供了与C和Objective-C类似的基础数据类型,包括整形Int、浮点数Double和Float、布尔类型Bool以及字符串类型String。Swift还提供了两种更强大的基本集合数据类型,Array和Dictionary,更详细的内容可以参考:Collection Types(中文教程:集合类型)。
跟C语言一样,Swift使用特定的名称来定义和使用变量。同样,Swift中也可以定义常量,与C语言不同的是,Swift中的常量更加强大,在编程时使用常量能够让代码看起来更加安全和简洁。
除了常见的数据类型之外,Swift还集成了Objective-C中所没有的“元组”类型,可以作为一个整体被传递。元组也可以成为一个函数的返回值,从而允许函数一次返回多个值。
Swift还提供了可选类型,用来处理一些未知的不存在的值。可选类型的意思是:这个值要么存在,并且等于x,要么根本不存在。可选类型类似于Objective-C中指针的nil值,但是nil只对类(class)有用,而可选类型对所有的类型都可用,并且更安全。可选类型是大部分Swift新特性的核心。
可选性类型只是Swift作为类型安全的编程语言的一个例子。Swift可以帮助你更快地发现编码中的类型错误。如果你的代码期望传递的参数类型是String的,那么类型安全就会防止你错误地传递一个Int值。这样就可以让编程人员在开发期更快地发现和修复问题。
1、常量和变量
常量和变量由一个特定名称来表示,如maximumNumberOfLoginAttempt 或者 welcomeMessage。常量所指向的是一个特定类型的值,如数字10或者字符”hello”。变量的值可以根据需要不断修改,而常量的值是不能够被二次修改的。
常量和变量的声明:
常量和变量在使用前都需要声明,在Swift中使用let关键词来声明一个常量,var关键词声明一个变量。如下面例子let maximumNumberOfLoginAttempts = 10 var 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几乎都可以隐式的确定变量或常量的类型,详见: Type Safety and Type Inference。
而上面的welcomeMessage的例子中:
初始化值没有被给出,所以更好的办法是指定welcomeMessage变量的类型而不是让Swift隐式推导类型。
常量和变量的命名
Swift中可以使用几乎任何字符来作为常量和变量名,包括Unicode,比如:
let π = 3.14159 let 你好 = "你好世界" let = "dogcow"
但是名称中不能含有:
- 数学符号,
- 箭头,
- 无效的Unicode,
- 横线-和制表符,
- 且不能以数字开头,尽管数字可以包含在名称里。
变量和常量也不能互换。
注:如果你想用Swift保留字命名一个常量或者变量,你可以用 ` 符号把命名包围起来。尽管如此,除非处于特别的意图,尽量不要使用保留字作为变量/常量名。可以改变变量的值为它声明的类型的其它值,如下的例子里,变量friendlyWelcome的值从“Hello!”被修改为”Bonjour!”:
var friendlyWelcome = “hello!” friendlyWelcome = “Bonjour!” // friendlyWelcome is now “Bonjour!”
与变量不同的是,常量的值一旦确定就不能修改。如果想尝试改变一个常量的值,编译代码时就会报错
let languageName = “Swift” languageName = “Swift++” // this is a compile-time error – languageName cannot be changed
输出常量和变量
Swift使用println来输出变量或者常量:(看起来很方便)println(friendlyWelcome) // prints “Bonjour!”println是一个全局函数,用来输出一个值,最后输出一个换行。在Xcode中,println输出在控制台中。print函数也类似,只不过最后不会输出换行。
println("This is a string") // prints "This is a string"
println函数还可以格式化输出一些日志信息,就像是Cocoa中NSLog函数的行为一样,可以包括一些常量和变量本身。
Swift在字符串中插入变量名作为占位符,使用反斜杠\和小括号()来提示Swift替换变量/常量名为其实际的值,如:
println(“The current value of friendlyWelcome is (friendlyWelcome)”) // prints “The current value of friendlyWelcome is Bonjour!”注:关于格式化字符的详见 String Interpolation
2、注释
不参与编译的语句称为注释,注释可以提示你代码的意图。Swift中的注释和C语言中的一样
单行注释
//this is a comment
/* this is also a comment,but written over multiple lines */
/* this is the start of the first multiline comment /* this is the second,nested multiline comment */ this is the end of the first multiline comment */这样可以方便地在大段已注释的代码块中继续添加注释
3、分号
和其它一些编程语言不同,Swift不需要使用分号 ; 来分隔每一个语句。当然你也可以选择使用分号,或者你想在一行中书写多个语句。
let cat = ""; println(cat) // prints ""
4、整数
整数就是像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 UInt8 let maxValue = UInt8.max // maxValue is equal to 255,and is of type UInt8
Int类型
一般来说,编程人员在写代码时不需要选择整数的位数,Swift提供了一种额外的整数类型Int,是和当前机器环境的字长相同的整数位数
在32位机器上,Int和Int32一样大小
在64位机器上,Int和Int64一样大小
除非你确实需要使用特定字长的正数,尽量使用Int类型。这保证了代码的可移植性。即使在32位的平台上,Int也可以存储-2,147,483,648 到2,647范围内的值,这对大部分正数来讲已经足够了。
UInt类型
Swift还提供了一种无符号类型UInt,同理也是和当前机器环境的字长相等。
在32位机器上,UInt和UInt32一样大小
在64位机器上,UInt和UInt64一样大小
注:只有显式的需要指定一个长度跟机器字长相等的无符号数的时候才需要使用UInt,其他的情况,尽量使用Int,即使这个变量确定是无符号的。都使用Int保证了代码的可移植性,避免了不同数字类型之间的转换。详见Type Safety and Type Inference.
5、浮点数
浮点数就是像3.14159,0.1,-273.15这样带分数的数字。分为Double和Float两种,其中Double的精度更高。
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
7、数值量表达
整型常量可以写成:
一个十进制数,不带前缀
一个二进制数,用前缀0b
一个八进制数,用0o前缀
一个十六进制数,以0x前缀
用这些整型常量来表达十进制值的17:
let decimalInteger= 17 let binaryInteger = 0b10001 // 17 in binary notation let octalInteger = 0o21 // 17 in octal notation let hexadecimalInteger = 0x11 // 17 inhexadecimal notation
浮点文本可以是十进制(不带前缀)或十六进制(以0x前缀)。它们必须始终具有在小数点的两侧(或十六进制数)。他们也可以有一个可选的指数,由一个大写或小写e表示十进制浮点数表示,或大写或小写p表示十六进制浮点数
为十进制数用的exp指数,基数乘以10exp:
1.25e2表示1.25×10 2,或者125.0.
1.25e-2表示1.25×10 -2,或者0.0125.
为十六进制数与EXP的指数,基部数乘以2EXP:
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
8、数据类型转换
使用Int类型的代码中的所有通用的整型常量和变量,即使它们是非负的。
在日常生活中使用默认的整数类型是指整型常量和变量是在代码中直接互操作,并将匹配的类型推断为整数值。
可以存储在一个整数常量或变量的范围根据每个数值类型是不同的。
一个Int8常量或变量可以存储数-128到127之间的数,而一个UInt8常量或变量可以存储0到255之间的数字。错误的赋值会让编译器报错:
let cannotBeNegative: UInt8 = -1 // UInt8 cannot store negative numbers,and so this will report an error let tooBig: Int8 = Int8.max + 1 // Int8 cannot store a number larger thanits maximum value,// and so this will also report an error
因为每个数字类型可以存储不同范围的值,你必须选择加入在逐案基础上数值类型的转换。
这种选择适用的做法可以防止隐藏的转换错误,并帮助作出明确在你的代码的类型转换意图。
要转换一个特定的数字类型到另一个,你初始化与现有值所需类型的新号码。在下面的例子中,恒定twoThousand是类型UInt16的,而常数1是类型UINT8的。它们不能被一起直接加入的,因为它们是相同类型的不。相反,该示例调用UInt16的(一个)来创建一个变量的值初始化的新UInt16的,并且使用这个值来代替原来的:let twoThousand: UInt16 = 2_000 let one: UInt8 = 1 let twoThousandAndOne= twoThousand + UInt16(one)输出常数(twoThousandAndOne)推断为类型UInt16的,因为它是两个UInt16的值的总和。
整数和浮点数转换
let three = 3 let pointOneFourOneFiveNine= 0.14159 let pi = Double(three) +pointOneFourOneFiveNine // pi equals 3.14159,and is inferred to beof typde Double这里,常数3的值被用来创建Double类型的新值,从而使两侧都是相同类型。如果没有这个转换,语句则非法。
反过来也是如此浮点到整数的转换,在一个整数类型可以用double或float值进行初始化:
let integerPi= Int(pi) // integerPi equals 3,and is inferred tobe of type Int
以这种方式初始化一个新的整数值,原始的浮点值总是被截断。
这意味着,4.75变为4,和-3.9变为-3。
9、类型别名
类型别名为现有类型定义的替代名称。您可以使用typealias关键字定义类型别名。当你使用的类型名称更符合上下文时,可以定义如:
typealias AudioSample = UInt16
一旦你定义了一个类型别名,你可以在任何会使用原来的名称地方使用别名:
var maxAmplitudeFound= AudioSample.min
// maxAmplitudeFound is now 0
这里,AudioSample被定义为一个UInt16的别名。因为它是一个别名,调用AudioSample.min实际上是调用UInt16.min,给maxAmplitudeFound变量赋初始值0。
10、布尔类型
Swift中的布尔类型使用Bool定义,值分别是true和false:
let orangesAreOrange = true
let turnipsAreDelicIoUs = false
跟Int和Double类型一样,在定义布尔类型的时候不需要显式的给出数据类型,只需要直接赋值为true或false即可
。布尔类型在条件语句中特别适用,比如在if语句中
像if语句这样的条件语句,我们会在之后的章节有详细介绍。
if turnipsAreDelicIoUs { println("Mmm,tasty turnips!") } else { println("Eww,turnips are horrible.") } // prints "Eww,turnips are horrible."Swift的类型安全策略会防止其他非布尔类型转换为布尔类型使用,比如
let i = 1 if i { // this example will not compile,and will report an error它会报错!但在有些编程语言中是可行的。
但是如下的定义是正确的:
let i = 1 if i == 1 { // this example will compile successfully }i == 1的结果就是一个布尔类型,所以可以在这里使用。上面的例子也是一个Swift类型安全的例子。
11、元组类型
元组类型可以将一些不同的数据类型组装成一个元素,这个元素可以用来作为函数的返回值返回包含多种数据类型
的值。
在下面的例子中,(404,“Not Found”) 是一个HTTP状态码,表述的是404错误,具体含义是页面未找到
let http404Error = (404,“Not Found”) // http404Error is of type (Int,String),and equals (404,“Not Found”)这个元组由一个Int和一个字符串String组成,这样的组合即包含了数字,也包含了便于人们认知的字符串描述。
编程人员可以随意地创建自己需要的元组类型,比如 (Int,Int,Int),或者(String,Bool)等。
可以通过如下方式分别访问一个元组的值:
let (statusCode,statusMessage) = http404Error println("The status code is \(statusCode)") // prints "The status code is 404" println("The status message is \(statusMessage)") // prints "The status message is Not Found"如果仅需要元组中的个别值,可以使用(_)来忽略不需要的值
let (justTheStatusCode,_) = http404Error println("The status code is \(justTheStatusCode)") // prints "The status code is 404"另外,也可以使用元素序号来选择元组中的值,注意序号是从0开始的
println("The status code is \(http404Error.0)") // prints "The status code is 404" println("The status message is \(http404Error.1)") // prints "The status message is Not Found"在创建一个元组的时候,也可以直接指定每个元素的名称,然后直接使用元组名.元素名访问,如:
let http200Status = (statusCode: 200,description: "OK") println("The status code is \(http200Status.statusCode)") // prints "The status code is 200" println("The status message is \(http200Status.description)") // prints "The status message is OK"
元组类型在作为函数返回值的时候特别适用,可以为函数返回更多的用户需要的信息。
在一个值可能不存在的时候,可以使用可选类型。这种类型的定义是:要么存在这个值,且等于x,要么这个值不存在。
这种类型在C和Objective-C中是不存在的,Objective-C中有一个相似的类型,叫nil,但是仅仅对对象有用。
下面给出一个例子,在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语
句中它将返回true,否则返回false。如果你已经检测确认该值存在,那么可以使用或者输出它,在输出的时候只
需要在名称后面加上感叹号(!)即可,意思是告诉编译器:我已经检测好这个值了,可以使用它了。如:
convertedNumber { println("\(possibleNumber) has an integer value of \(convertedNumber!)") } else { println("\(possibleNumber) could not be converted to an integer") } // prints "123 has an integer value of 123"选择性绑定
使用一个变量或常量来绑定一个可选类型,在if和while语句中,来检查该值是否存在,然后再继续使用它,绑定
方法如下:
if let constantName = someOptional { statements }那么上一个例子也可以改写为:
if let actualNumber = possibleNumber.toInt() { println("\(possibleNumber) has an integer value of \(actualNumber)") } else { println("\(possibleNumber) could not be converted to an integer") } // prints "123 has an integer value of 123"上述代码理解起来不难:如果这个可选Int类型包含一个值,那么定义一个常量actualNumber来等于这个值,并在
后续代码中直接使用。
nil
可以给可选类型指定一个特殊的值nil:
var serverResponseCode: Int? = 404 // serverResponseCode contains an actual Int value of 404 serverResponseCode = nil // serverResponseCode now contains no value如果你定义了一个可选类型并且没有给予初始值的时候,会默认设置为nil
var surveyAnswer: String? // surveyAnswer is automatically set to nil
注: Swift 的nil不同于Object-C中的nil. Object-C中,nil是一个指针指向不存在的对象。
Swift中,nil不是指针而是一个特定类型的空值。任何类型的可选变量都可以被设为nil,不光是指针。
隐式强制使用可选类型
但是在一些情况下,可选类型是一直有效的,那么可以通过定义来隐式地去掉类型检查,强制使用可选类型。
这些可选类型被成为隐式解包的可选类型。你可以直接在类型后面加! 而不是?来指定。
隐式解包的可选类型主要用在一个变量/常量在定义瞬间完成之后值一定会存在的情况。
这主要用在类的初始化过程中,详见Unowned References and Implicitly Unwrapped Optional Properties.
隐式解包的可选类型本质是可选类型,但是可以被当成一般类型来使用,不需要每次验证值是否存在。
如下的例子展示了可选类型和解包可选类型之间的区别。
let possibleString: String? = "An optional string." println(possibleString!) // requires an exclamation mark to access its value // prints "An optional string." let assumedString: String! = "An implicitly unwrapped optional string." println(assumedString) // no exclamation mark is needed to access its value // prints "An implicitly unwrapped optional string."直接在变量后面加上!,String!,这样可以确保该值一定存在。
隐式转换同样也可以使用if语句来检测
if assumedString { println(assumedString) } // prints "An implicitly unwrapped optional string."或者选择性绑定
if let definiteString = assumedString { println(definiteString) } // prints "An implicitly unwrapped optional string."
13、使用断言
可选类型让编程人员可以检测一个值是否存在,然后使用代码来处理不存在的情况。但是有些情况下,如果一个值
不存在会直接影响代码的执行,这个时候就需要使用断言。只有在满足特定条件的时候,代码才会继续执行。
使用断言调试
断言是一种实时检测条件是否为true的方法。如果这个条件为false,那么代码将会中断执行。
在Xcode中,在调试的时候如果中断,可以通过查看调试语句来找出问题所在。
使用全局函数assert来使用断言调试,如:
let age = -3 assert(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)