英文版PDF下载地址http://download.csdn.net/detail/tsingheng/7480427
Swift是用来开发iOS和OS X应用的新语言,但是许多地方用起来跟C或者OC是差不多的。
Swift提供了C语言和OC的基本数据类型,比如整型用Int,浮点型用Double或者Float,布尔型用Bool,字符串文本用String,Swift还提供了两种集合类型,Array和Dictionary,后面会介绍。
Swift也跟C一样用唯一的变量名来存储与使用数据,还能使用一些值不会变的变量,应该说是常量,比C里面的常量要强大。在涉及到一些值不会发生变化的数据时使用常量类型能让代码更安全整洁。
除了跟C和OC相似的类型之外,Swift还增加了一些OC中没有的类型。比如元组,使用元组可以创建或者传递多个值。函数返回值使用元组就可以返回多个值了。
Swift还增加了可选类型。可选类型的变量要么有个确切值,要么是没有值。有点儿像OC里面值为nil的指针,但是OC里面指针只能用于对象,Swift可选类型能用于任何数据类型。可选类型比OC中的nil指针更安全更好用,Swift很多核心牛逼的特性都要指望他。
可选类型也能说明了Swift是一种类型安全的语言。Swift能让程序员清楚知道自己使用的是什么类型。如果代码中需要一个String对象,类型安全机制可以保证Int之类的非String类型对象被传过来。类型安全机制能让你在写代码的时候就能发现并纠正一些错误。
1.常量和变量
常量跟变量都由名称跟值组成,常量的值一旦设置就不允许再改变。变量就没有这个限制了,只要类型没错就能为所欲为。
常量和变量的声明
常量和变量在使用之前都必须声明。常量用let声明,变量用var声明。这边有个例子用常量和变量来记录用户尝试登录次数。
let maximumNumberOfLoginAttempts = 10
var currentLoginAttempt = 0
这个例子里面maximumNumberOfLoginAttempts是被声明为常量的,因为最多允许登录尝试次数是固定的,不会变的。而currentLoginAttempt表示用户当前已经尝试的次数,是随着用户登录尝试要增加的,所以要被定义成变量。
声明变量或者常量的时候也可以一行声明多个,中间用逗号分隔。比如 var x = 0.0, y = 0.0, z = 0.0
NOTE 如果代码里面要用到不会改变的值,尽量都用let声明成常量,只有需要改变的才声明成变量。
类型标注
声明变量或者常量的时候还可以加个类型标注,来限定这个变量或者常量可以存储什么类型的值。写法是在变量名或常量名后面加上冒号空格再加存储类型。举个例子,我要声明一个存储String类型的变量,代码就是 var welcomeMessage: String,冒号的意思是“...类型为...”,所以前面的代码可以翻译成:“声明一个类型为String的变量welcomeMessage”。“类型为String”意思是说“可以存储任何的String值”。
welcomeMessage现在可以被赋值,不会报错了。 welcomeMessage = “Hello”
NOTE 实际上这个类型标注可能很少用,如果在声明变量或者常量的时候就给他个初始值,编译器就能自己推断出来变量或者常量的类型是什么了。这个在后面类型安全与类型推断里面会江到。上面这个例子中welcomeMessage没有初始值,所以编译器没有办法推断出来他是什么类型,也就需要用类型标注来指明变量类型了。
变量或常量命名
Swift变量常量命名很自由,甚至可以用中文或者表情字符来命名。
let π = 3.14159
let 你好 = "你好中国"
let 狗狗表情 = "dogcow"
变量或者常量的名字不能重复声明,类型也不能变,变量跟常量也不能互相转变。
NOTE 如果你想用Swift关键字同名的名字,你可以在名字前后加上倒引号(好像是数字1前面那个键)就行了,不过要尽量避免使用关键字,除非说是没的选了(比如说领导要求的)。
变量的值可以改成跟现有值相同类型的值。
var friendlyWelcome = "Hello!"
friendlyWelcome = "Bonjour!"
// friendlyWelcome is now "Bonjour!"
let languageName = "Swift"
languageName = "Swift++"
// this is a compile-time error - languageName cannot be changed
可以使用println函数来输出常量或者变量的值。
println(frientlyWelcome)
//输出“Bonjour”
println函数可以输出任何字符串。
println("This is a string")
// prints "This is a string"
Swift使用字符串填补的方式在长字符串中使用常量或者变量。使用方法是用括号把变量名括起来,前面在加个反斜杠,然后直接放入字符串中。
println("The current value of friendlyWelcome is \(friendlyWelcome)")
// prints "The current value of friendlyWelcome is Bonjour!"
2.注释
使用注释可以在代码中加入一段不被执行的文字。编译器编译的时候会把所有的注释都忽略掉。
Swift的注释跟C的注释非常像,单行注释用双斜杠打头。比如 // 这是一行注释
多行注释用/*开头,*/结尾
/* 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 = "cat"; println(cat)
整型数是指没有小数的数字,比如42啊,-23啊之类的。整型要么是有符号的(正数,0,负数)要么是无符号的(正数或者0)。
Swfit的无符号数提供8位,16位,32位以及64位的版本。这些类型的命名方式跟C是差不多的。比如8位的无符号整型是UInt8,32位有符号整型是Int32。跟其他类型一样,这些类型首字母都要大写。
整型范围
你可以通过整型类型的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存储范围是-2147483648到2147483647,这范围足够大了。
UInt
Swift还提供了一种没有符号的整型UInt,跟Int一样长度由程序运行的平台决定。
NOTE 只有当你确实需要使用无符号数的时候采用UInt,不然尽量使用Int,就算你知道要保存的值不会有负数。使用Int能增强代码的一致性和通用性,避免了各种类型数字计算的时候再去类型转换。
5.浮点数
浮点数就是带小数部分的数字。比如3.1415,-273.354。
浮点型能存储的数字范围比整型大得多。并且可以存储比整型数最大值更大的数,和比整型数最小值更小的值。Swift提供两种带符号的浮点数类型。
NOTE Double类型提供至少15位小数的精度,Float的小数精度只有6位。选哪种类型要根据你要处理的数据的实际情况。
6.类型安全与类型推断
Swift是一种类型安全的语言。类型安全的语言要让你清楚自己使用的变量是什么类型。如果变量是String类型,就不能赋Int值给他哦。
因为Swift是类型安全的,所以编译器在编译代码的时候要进行类型检查,对于任何类型匹配不正确的地方都会报错。这个特性能让你在开发的时候就发现并纠正很多错误了。
当你要处理各种各样不同类型的数据的时候,类型检查能帮你避免很多错误。但是并不是说你必须在声明每一个常量或者变量的时候就要指定对应的类型。如果你不指定,Swift会自动推断出对应的类型。类型推断可以让编译器在编译的时候通过你提供的值来自动推断出其所对应的数据类型。
因为有了类型推断,Swift里面的类型声明就比C或者OC这样的语言里面少的多了。
当你声明常量或者变量的同时还提供初始值得时候类型推断就非常好用了。通常要在声明变量或者常量的时候给他提供一个字面值。(字面值就好比是个立即数,不需要额外计算的。比如下面例子中的42或者3.14159。)
比如如果你把字面值42赋给一个刚声明的常量,但是不指定常量类型,Swift会推断出来这个常量是Int类型的,因为你提供的初始值是Int类型的。
let meaningOfLife = 42
// meaningOfLife is inferred to be of type Int
let pi = 3.14159
// pi is inferred to be of type Double
如果你在表达式里用整数和小数相加,结果推断出来的是Double。
let anotherPi = 3 + 0.14159
// anotherPi is also inferred to be of type Double
7.数字字面值
整数字面值可以用下面几种方式表示:
比如下面这几个数字的十进制值都是17
let decimalInteger = 17
let binaryInteger = 0b10001 // 17 in binary notation
let octalInteger = 0o21 // 17 in octal notation
let hexadecimalInteger = 0x11 // 17 in hexadecimal notation
对于十进制来说如果指数是exp,就表示用基数乘以10的exp次方:
对于十六进制数来说如果指数是ext,就表示用基数乘以2的exp次方:
下面的几个浮点数字面量都是十进制的12.1875
let decimalDouble = 12.1875
let exponentDouble = 1.21875e1
let hexadecimalDouble = 0xC.3p0
8.数字类型转换
要存储数字的时候尽量都用Int,即便是你知道要存储的值不会是负数。如果你每次都用默认的整型类型来定义常量和变量,这样你定义的这些变量跟常量的类型就能跟字面值推断出来的类型一样了,代码通用性就很强。
只有当你必须要用其他类型的整型的时候才去用,比如要在代码上用确切位数来保证性能,或者内存优化或者其他必要的优化。使用指定长度的类型可以帮助你避免一些不小心的值溢出,也可以为数据提供一份隐式的说明。
整型转换
对于整型常量或者变量,不一样的数字类型能存储的数据范围是不同的。Int8类型能存储的范围是-128到127,UInt8可以存储的范围是0到255。赋值的时候如果数据超出这个范围内,编译器会报错。
let cannotBeNegative: UInt8 = -1
// UInt8类型不能存储负数,所以会报错。UInt8 cannot store negative numbers, and so this will report an error
let tooBig: Int8 = Int8.max + 1
// <pre name="code" class="objc">Int8 不能存储超过最大值得数字,所以这里也会报错。<span style="font-family: Arial, Helvetica, sans-serif;">Int8 cannot store a number larger than its maximum value,</span>
// and so this will also report an error
因为各种数字类型能存储的数据范围不同,所以你就必须要根据具体情况来进行类型转换了。这样能防止隐式转换的错误,还能让代码里的类型转换意图变得清晰。
要转换成其他类型,你需要实例化一个需要转换的类型的实例并且用现有的值给他初始化。拿上面的例子来说,常量twoThousand是UInt6类型,而常量one是UInt8类型,因为类型不一样所以两个常量不能直接相加。下面例子用UInt16(one)来创建一个UInt16类型的实例并用one来初始化,然后用这个值跟twoThousand相加。
let twoThousand: UInt16 = 2_000
let one: UInt8 = 1
let twoThousandAndOne = twoThousand + UInt16(one)
类型(值)是调用类型的初始化器并传入一个初始值的默认方式。当然这需要有个前提,就是UInt16有一个接受UInt8参数的初始化器,所以才能用这个方式来用UInt8创建UInt16。所以这里你不能随便传其他类型的,必须是UInt16提供了相应的初始化器的才行。拓展现有的初始化器,添加新的类型参数支持(比方说要支持自己定义的类型)在后面的扩展部分里会介绍。
整型和浮点型的转换
整型和浮点型之间转换必须要显式转换。
let three = 3
let pointOneFourOneFiveNine = 0.14159
let pi = Double(three) + pointOneFourOneFiveNine
// pi=3.14159,所以pi被推断出来时Double类型。pi equals 3.14159, and is inferred to be of type Double
这个例子中常量three的被用来创建另外一个Double类型,这样加号两边的类型才一样。如果不转换,是不能相加的。
反过来也是一样的,Double或者Float类型的值也可以用来初始化一个整型的值。
let integerPi = Int(pi)
// integerPi equals 3, and is inferred to be of type Int
NOTE 数字变量或者常量相加的规则跟数字字面量相加规则是不一样的(还记得前面有个3+0.14159吗),字面量3可以直接跟字面量0.14159相加,因为字面量本身还没有一个确切的类型,只有当编译器计算和的时候才会去推断字面量的类型。
9.类型别名
类型别名是指使用现有类型的另外一种方式。类型别名使用关键字typealias来定义。当你想用更合适的方式来使用现有的类型的时候,类型别名就派上用场了。比方说你要在外部代码中使用指定长度的数据。 typealias AutioSample = UInt16
只要你定义了类型别名,那其他只要能用原名的地方都能用别名。
var maxAmplitudeFound = AudioSample.min
// maxAmplitudeFound is now 0
10.布尔型
Swift有一个基本的布尔类型,叫Bool,布尔值是一种逻辑值,要么是true要么是false,Swift提供了两个布尔的常量值,没错,你猜对了,true和false。
let orangesAreOrange = true
let turnipsAreDelicious = false
当你使用if之类的条件判断语句的时候布尔值就显得很需要了。
if turnipsAreDelicious {
println("Mmm, tasty turnips!")
} else {
println("Eww, turnips are horrible.")
}
// prints "Eww, turnips are horrible."
Swift的类型安全机制会保证不能再需要Bool值得地方用其他类型的值来代替。下面的例子就会报错。
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的比较结果是Bool类型的,所以第二种方式可以通过类型安全检测,类似i==1这样的比较运算会在后面的基本运算里介绍。
跟其他的类型安全的例子一样,这个也能避免一些偶然的错误,保证代码的意思清晰明了。
11.元组
元组能把好几个值放在一起组成一个复合值。元组里面的值可以是任何类型,各个值还不需要类型相同。举个例子,(404, "NotFound")这个元组用来表示一个HTTP状态码。HTTP状态码是指你向一个HTTP服务器请求页面的时候服务器返回的一个特殊值,如果你请求的页面不存在,服务器就会返回404 Not Found。
let http404Error = (404, "Not Found")
// http404Error is of type (Int, String), and equals (404, "Not Found")
你可以使用任何一组类型来创建元组,你想要任何不同的类型,都能满足你。没有什么可以阻挠你创建(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"
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"
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"
元组用来作为函数的返回值的时候特别有用。比方说有个检索页面的函数,返回(Int, String)类型的元组来表示检索页面成功还是失败。通过返回带有两个值的元组,就能比只返回单个值的函数提供更多信息。更多介绍,请查看多返回值函数。
NOTE 元组对于暂时组装一些相关的数据非常有用。他不需要你去定义一些复杂的数据结构。如果你的数据结果不只是暂时使用比方说还得存盘,这个时候使用创建一个类或者结果体会更合适。更多相关信息请查阅类和结构。
12.可选值
如果说某个值有可能不存在,那你可以使用可选值,可选值表示这里有个值等于多少多少,或者说这里没有值。
NOTE C跟OC里面是不存在可选值的概念的。OC里面跟可选值最接近的特性就是方法要么返回nil要么返回对象,返回nil表示没有可用的对象。但是这只针对对象有效,对于结构体,基本类型或者枚举类型是不适用的。对于这些类型,OC的方法返回了一个特别的值(比如说NSNotFound)来说明这里没有值。这种方式要求调用方法的地方知道这里会返回特殊值,并且要记得去判断。Swift的可选值就没有类型的限制,也就不需要额外特殊额常量了。
这边有个例子,Swift的String类型有个方法叫做toInt,用来把字符串的值转换成Int值。但是,并不是所有的字符串都能转换成数字的,“123"可以转换成123但是”hello, china“显然是转不了的。下面的例子就用toInt方法来吧String转换成Int:
let possibleNumber = "123"
let convertedNumber = possibleNumber.toInt()
// convertedNumber is inferred to be of type "Int?", or "optional Int"
if语句和强行拆箱
你可以使用if语句来验证一个可选值是不是包含有值。如果有值,返回的是true,如果没有值返回的就是false。
如果你确定可选值有值,你可以在变量名后面加个感叹号来取得里面的值。感叹号的意思相当于:”小样儿我知道你带的有钱,交出来哥要用。“这个就是可选值强制拆箱。
if 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"
NOTE 如果用感叹号去获取没有值的可选值,会发生运行时错误。一定要当你确定可选值有值的时候才使用感叹号。想想你鼓起勇气打劫了个穷光蛋,一毛钱没捞着还进去了。
可选值绑定
你也可以用可选值绑定来验证可选值里面是不是有值,通过这种方式可以让可选值暂时存储在一个临时的常量或者变量里。可选值绑定可以用在if或者while的条件语句中判断可选变量里面是不是有值,并且顺便把值取出来放到常量或者变量里面。if跟while在控制流里面介绍,一般有点儿基础的应该都可以跳过了。
if中使用可选值绑定是下面这样的
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"
如果转换成功,常量actualNumber就能在if的大括号里面用了,而且actualNumber已经用转换方法返回的值初始化了,所以就没有必要再用感叹号去获取常量的值了。这个例子中actualNumber只是简单的用来输出转换的结果。
在使用可选值绑定的时候可以用常量也可以用变量。如果你想在if第一个大括号里修改actualNumber的值,就用if var actualNumber代替,然后可选值包含的值就是个变量而不是常量了。
nil
如果你要把一个可选变量变成没有值的状态,给他赋值为ni就可以了。
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
隐式拆箱可选值
上面说的,可选值是说可以没有值的常量或者变量。可选值可以用if语句来检测是不是有值,也可以用可选绑定来拆箱并且获取其中的值。
有时候有的可选值里面很明显总是会有值的,在这种情况下就没有必要每次使用的时候都去检查来拆箱获取值,因为我确定他总是会有值得。这种可选值被称为隐式拆箱可选值。定义这种类型要在类型后面加感叹号,而不是问号。
当某个可选值被定义的时候就被赋值,而且以后都能确定他总是会有值,这个时候隐式拆箱可选值就可以使用。最开始使用隐式拆箱可选值的地方时在类初始化的地方,更多内容在无引用和隐式拆箱可选值属性里面介绍。
隐式拆箱可选值也就是一种可选值,但是还能像非可选值一样来使用,而不想要每次使用的时候都去拆箱。下面举个例子说明可选字符串与隐式拆箱可选字符串的区别。
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."
NOTE 如果你试图访问一个没有值的隐式拆箱可选变量,会产生运行时错误,就像你在一个没有值得可选变量后面加个感叹号一样。
你还可以像使用普通可选值一样使用隐式产想可选值。比如说用If判断其是否有值
if assumedString {
println(assumedString)
}
// prints "An implicitly unwrapped optional string."
13.断言(Assetions)
可选类型可以让你检测值是否存在,然后处理值不存在的情况。但是在有些情况下,如果值不存在就没办法进行,或者有值但是这个值不符合条件。这种情况下你可以触发断言来结束执行,从而可以调试为什么值不存在。
使用断言调试
断言是指在运行期间检测一个逻辑值是否为true。也就是说断言断定那个条件是true。你可以使用断言来保证程序执行的时候只有当满足了某个条件的时候才继续往下执行,如果条件表达式计算结果为true,程序照常执行,如果结果为false,就不再执行了,应用也就退出了。
如果你在调试环境中运行代码并且触发了断言,那你可以清楚的看到哪里的条件没通过,并且可以查看当断言触发的时候应用时什么状态。断言还可以接受你提供的一些调试信息。通过调用全局函数assert来使用断言。第一个参数需要是范围true或者false的表达式,第二个参数的信息可以在表达式返回false的时候输出。
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
注意第二个参数不能使用字符串填补。不过第二个参数可以省略,比如asset(age >= 0)
什么时候要用断言
如果说某个地方你一定需要满足什么条件才能继续执行的时候就可以用断言。其中包括以下场景:
在使用下标语法的时候数字下标的值不能太大也不能太小。
调用函数的时候如果传递非法参数函数就不能完成任务。
有个可选值当时是nil,但是后面代码的执行需要他有值。
NOTE 当某些条件达不到的时候断言会让应用终止,让应用程序终止肯定不是最好的方式。但是在开发阶段还是保证那些条件被注意到的有效的方式。产品发布了就不要这样做了。
本章完。下章地址: 4.基本运算符