Swift是一种新出现的开发iOS,OS X 和 watchOS应用程序的编程语言。尽管如此,有 C 和 Objeictive-C 开发经验的你,会从Swift很多部分中发现熟悉的感觉。
基本类型:Int、Double、Float、Bool、String
集合类型:Array、Set、Dictionary
常量:使代码更安全、更清晰
元组:
Optional types:表示有值或没值
Swift是类型安全语言
变量与常量
声明
变量与常量必须先声明,再使用。
- let 声明常量
- var 声明变量
<pre><code>
let maximumNumberOfLoginAttempts = 10
var currentLoginAttempt = 0
</code></pre>
在一行中声明多个变量或常量,使用逗号分隔
<pre><code>
var x = 0.0, y = 0.0, z = 0.0
</code></pre>
类型注解
在变量或常量名后,添加冒号,空格,类型名称
<pre><code>
var welcomeMessage: String
</code></pre>
在一行中声明多个同类型的变量
<pre><code>
var red, green, blue: Double
</code></pre>
注:在实际使用中,类型注解很少使用。在定义变量和常量时,如果赋予初始值,Swfit几乎都能推断出类型,细节在“类型安全与类型推断”中。
命名规范
几乎支持所有的字符,包括Unicode字符。
<pre><code>
let π = 3.14159
let 你好 = "你好世界"
let 🐶🐮 = "dogcow"
</code></pre>
不能包含空白字符、数学符号、箭头、连字符-、画框线、私有或无效的Unicode code points;不能已数字开始,但是数字可以用在除开始外的其它位置。
不能声明同名的变量或常量名称,不能存放与声明时不同类型的数据,也不能将常量变为变量-反之亦然。
注:如果名称与Swift的保留关键字相同,使用`(反单引号)扩住名称。不过,除非别无选择,最好避免使用关键字。
输出
可以使用print(_:)函数输出变量或常量的值
<pre><code>
var friendlyWelcome = "Bonjour!"
print(friendlyWelcome)
// prints "Bonjour!"
</code></pre>
print(\_:)
是全局函数,在输出内容后,行尾输出换行符。
print(\_:appendNewline:)
同print(\_:)
,只是有参数控制是否输出换行符
在输出字符串中使用“\(变量或常量名称)”的形式作为占位符,输出变量或常量的值
<pre><code>
print("The current value of friendlyWelcome is (friendlyWelcome)")
// prints "The current value of friendlyWelcome is Bonjour!"
</code></pre>
注释 Comments
单行注释:
<pre><code>
// 注释内容到行尾
</code></pre>
多行注释:/* 注释内容 */,多行注释可嵌套使用
<pre><code>
/* 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 */
</code></pre>
分号 Semicolons
Swift不需要在每个语句结尾输入分号,不过你这么做也不会出错。只有一种情况是需要分号的:在一行中输入多条独立的语句
<pre><code>
let cat = "🐱"; print(cat)
// prints "🐱"
</code></pre>
整数 Integers
包括有符号整数:正数、0、负数;无符号整数:正数、0
Swift的整数:Int8、Int16、Int32、Int64,UInt8、UInt16、UInt32、UInt64
整数边界 Integer Bounds
使用每个整数类型,可以获得该类型的最大值(max)与最小值(min)属性:
<pre><code>
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
</code></pre>
浮点数 Floating-Point Numbers
- Double 表示一个 64-bit 浮点数,15位数字精度
- Float 表示一个 32-bit 浮点数,6位数字精度
类型安全与类型推断
Swift是类型安全的,在编译时将进行类型检查并表示出不匹配的代码。
类型检查并不意味着需要你在声明时指定每个变量或常量的类型。如果不指定,Swift将使用类型推断(type inferrence)推断出合适的类型
由于有了类型推断,Swift在声明时需要的类型信息要远少于 C 和 Objective-C。
在声明变量或常量并赋予初始值时,类型推断尤其有用。
如下,如果给声明常量meaningOfLife并赋值42,但是没有类型信息,Swift会推断出常量的类型为Int,因为42看起来更像一个整数。
<pre><code>
let meaningOfLife = 42
// meaningOfLife is inferred to be of type Int
</code></pre>
同样地,如果指定一个浮点数,Swift推断为Double
<pre><code>
let pi = 3.14159
// pi is inferred to be of type Double
</code></pre>
对于浮点数,Swift总是使用Double,如果需要Float,则使用
<pre><code>
let pi = Float(3.14159)
// pi is inferred to be of type Float
</code></pre>
如果组合使用整数和浮点数,最终Swift会推断为Double
<pre><code>
let anotherPi = 3 + 0.14159
// anotherPi is also inferred to be of type Double
</code></pre>
在这里,3会根据表达式推断为Double
数字 Numeric Literals
整数有以下的写法
- 十进制:十进制数字 (0123456789),没有前缀
- 二进制:二进制数字(01),前缀为0b
- 八进制:八进制数字(01234567),前缀为0o
- 十六进制:十六进制数字(0123456789abcdef),前缀为0x
<pre><code>以下所有值在十进制中都是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
</code></pre>
浮点数可以使用十进制数(没有前缀)和十六进制拼写,中间必须有个小数点;同时可以有指数形式的写法:十进制用e/E,十六进制用p/P,后面跟指数。
- 十进制指数,使用基数乘以10exp:
1.25e2 表示 1.25 x 102, or 125.0
1.25e-2 表示 1.25 x 10-2, or 0.0125. - 十六进制指数,使用基数乘以2exp:
0xFp2 means 15 x 22, or 60.0.
0xFp-2 means 15 x 2-2, or 3.75.
<pre><code>以下所有值在十进制中都是12.1875
let decimalDouble = 12.1875
let exponentDouble = 1.21875e1
let hexadecimalDouble = 0xC.3p0
</code></pre>
数字可以包含格式符号_(下划线)以增强阅读性
<pre><code>
let paddedDouble = 000123.456
let oneMillion = 1_000_000
let justOverOneMillion = 1_000_000.000_000_1
</code></pre>
<pre><code>写成这样也不出错,但是小数点后面得跟数组,最前面(除符号)只能由前导0
let paddedDouble = -000123.456___e1__0___
let oneMillion = 0000000000000001_0_0_0_0_0_0______
let justOverOneMillion = 1_000_000______.0____0__0_000_1______
</code></pre>
数值类型转换 Numberic Type Conversion
写整数时一般使用Int(在XCode7beta上是32位)类型,对非负数也推荐这样用。
只有在需要的时候,才使用其他整数类型。例如:明确存储空间大小的外部数据、性能、内存或其他的优化
整数转换 Integer Conversion
每个整数类型都有一定的数值范围,超出范围的数在编译时将报错
<pre><code>
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 than its maximum value,
// and so this will also report an error
</code></pre>
要把一个整数转换为另一个类型,需要根据整数数值初始化一个新的目标类型整数。如下:不同类型的整数不能直接运算,要转换为相同类型后才能相加。
<pre><code>
let twoThousand: UInt16 = 2_000
let one: UInt8 = 1
let twoThousandAndOne = twoThousand + UInt16(one)
</code></pre>
SomeType(ofInitialValue)是根据传入值初始化类型数据的默认方法。在幕后,UInt16有个初始化方法接收UInt8类型数据作为初始化参数,所有UInt16才能根据UInt8数据生成新的UInt16数据。
整数-浮点数转换 Integer and Floating-Point Conversion
整数与浮点数之间的转换必须明确类型
<pre><code>
let three = 3
let pointOneFourOneFiveNine = 0.14159
let pi = Double(three) + pointOneFourOneFiveNine
// pi equals 3.14159, and is inferred to be of type Double
</code></pre>
浮点数在转换为整数时,将去掉小数部分,例如:4.75
变为整数为4
,-3.9
变为整数位-3
类型别名 Type Aliases
使用typealias
关键字可以给一个已经存在的类型定义一个别名
在根据上下文,给类型起一个更贴切的名字,这正是类型别名点用武之地,如:
<pre><code>
typealias AudioSample = UInt16
</code></pre>
一旦定义了别名,就可以想使用原始类型一样使用别名:
<pre><code>
var maxAmplitudeFound = AudioSample.min
// maxAmplitudeFound is now 0
</code></pre>
布尔类型 Booleans
Swift的布尔类型为Bool,布尔值是逻辑值,只有 true、false 两个常量值
<pre><code>
let orangesAreOrange = true
let turnipsAreDelicious = false
</code></pre>
Swift的Type Inference机制会推断出他们是Bool类型的数据
布尔值主要用在条件语句中,比如if
语句:
<pre><code>
if turnipsAreDelicious {
print("Mmm, tasty turnips!")
} else {
print("Eww, turnips are horrible.")
}
// prints "Eww, turnips are horrible."
</code></pre>
Swift的类型安全机制会阻止使用非布尔值替代Bool
,下例在编译时将会报错:
<pre><code>
let i = 1
if i {
// this example will not compile, and will report an error
}
</code></pre>
不过,这样使用就是正确的了
<pre><code>
let i = 1
if i == 1 {
// this example will compile successfully
}
</code></pre>
i == 1
的结果是Bool类型
元组 Tuples
元组是一个复合值,由多个值组成,每个组成元组的值可以是任意类型,不必都是同一个类型的值
例如(404, "Not Found")
,这个元组描述一个 HTTP 状态值,状态码404表示资源未找到
<pre><code>
let http404Error = (404, "Not Found")
// http404Error is of type (Int, String), and equals (404, "Not Found")
</code></pre>
元组(404, "Not Found")
包含一个Int
和一个String
,类型可以看成是(Int, String)
可以使用任意一组类型的排列创建元组,例如:(Int, Int, Int)
,或者(String, Bool)
,或者其他你想要的类型排列方式
使用如下方式使用元组内数据
<pre><code>
let (statusCode, statusMessage) = http404Error
print("The status code is (statusCode)")
// prints "The status code is 404"
print("The status message is (statusMessage)")
// prints "The status message is Not Found"
</code></pre>
如果只关注元组内的一部分数据,可以使用下划线(_)忽略不想要的部分
<pre><code>
let (justTheStatusCode, _) = http404Error
print("The status code is (justTheStatusCode)")
// prints "The status code is 404"
</code></pre>
或者,可以使用索引(从0开始)的方式访问元组内容
<pre><code>
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"
</code></pre>
也可以给元组内的值起个名字,这样就可以通过名字来访问了(给某个值起名,其他不起名也可以,我在playground中测了一下)
<pre><code>
let http200Status = (statusCode: 200, description: "OK")
<br />
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"
</code></pre>
Optionals (这个怎么翻译好?)
声明一个变量、返回结果、属性等可能处在有值、没有值两种状态:
- 没有值,在objc里就是nil
- 有值,就是他的内容
在 C 和 Objective-C中,不存在这个概念。比较接近的是在Objective-C中,一个方法可能返回nil,也可能返回一个对象。nil就意味着没有有效值。但是在Objective-C中,基础类型、结构、枚举作为方法返回值时就不能返回nil,只能返回特定的值(如NSNotFound)表示没有返回有效值。
下例就展示Optional在处理无效值时的使用方法
<pre><code>
et possibleNumber = "123"
let convertedNumber = Int(possibleNumber)
let unconvertNumber = Int("Hello World!")
// convertedNumber is inferred to be of type "Int?", or "optional Int", value is 123
// unconvertNumber is inferred to be of type "Int?", value is nil
</code></pre>
nil
可以给Optional变量赋值为nil,表示无值
<pre><code>
var serverResponseCode: Int? = 404
// serverResponseCode contains an actual Int value of 404
serverResponseCode = nil
// serverResponseCode now contains no value
</code></pre>
nil 不能用在非Optional的变量和常量上,如果想使用无值的变量或常量,那就把他声明为Optional
如果定义了一个Optional变量但是没有初始化,那么变量自动为nil
<pre><code>
var surveyAnswer: String?
// surveyAnswer is automatically set to nil
</code></pre>
注:Swift的nil 与 Objective-C的nil是不同的。在Objective-C中,nil表示一个指向不存在对象的指针;在Swift中,nil不是指针,他表示某一类型的值没有出现(没赋值)。任意Optional类型都可以赋值nil,而不仅仅是对象类型。
If Statements and Forced Unwrapping
使用if
语句与nil
比较能判断一个Optional 是否有值。可以使用等于操作符==
或 不等于操作符!=
执行判断
如果Optional没有值,那么就与nil
不相等
<pre><code>
if convertedNumber != nil {
print("convertedNumber contains some integer value.")
}
// prints "convertedNumber contains some integer value."
</code></pre>
如果确定Optional有值,就可以在名称后面紧跟叹号(!
)来访问他的值。这个就叫forced unwrapping(强制解包?)
<pre><code>
if convertedNumber != nil {
print("convertedNumber has an integer value of (convertedNumber!).")
}
// prints "convertedNumber has an integer value of 123."
</code></pre>
注:如果对无值的Optional使用
!
,将会引发运行时错误。所以在使用!
前一定要确保Optional 含有非nil
值
Optional Binding
使用Optional Binding检查Optional是否有值,如果有,可以创建一个临时的变量或常量来使用该值。可以用于if
和while
语句,检查Optional是否有值,并提取该值到一个变量或常量中
在if
语句中使用Optional Binding,如下:
<pre><code>
if let constantName = someOptional {
statements
}
</code></pre>
在Optional一节中possibleNumber
的例子,可以使用Optional Binding的方式重写,而不使用Forced Unwrapping
<pre><code>
if let actualNumber = Int(possibleNumber) {
print("'(possibleNumber)' has an integer value of (actualNumber)")
} else {
print("'(possibleNumber)' could not be converted to an integer")
}
// prints "'123' has an integer value of 123"
</code></pre>
上例可以解读为:如果Int(possibleNumber)返回的Optional Int有值,就将值赋值给常量
actualNumber
如果转换成功,常量actualNumber
在if
语句的第一个分支中变为可用。由于actualNumber
已经使用Optional的值初始化,所以这里就不需再使用!
后缀来访问其值。
再Optional Binding时可以使用常量或变量。如果想改变actualNumber
的值,则使用if var actualNumber =
代替上例中的if let actualNumber =
如果有多个Optional需要Optional Binding,可以把它们都写在一起(一行或多行),用逗号分隔。
<pre><code>
if let [constantName] = [someOptional], [anotherConstantName] = [someOtherOptional] {
[statements]
}
</code></pre>
Implicitly Unwrapped Optionals
如上所述,Optional表示变量或常量可以为“无值”。Optional可以使用if
语句检查是否有值;如果有值,也可以使用Optional Binding有条件的展开来访问其值
有时,从程序结构上来看Optional总是有值。这种情况下,因为可以安全地假定Optional一直有值,那么取消检查与解包Optional的值,对于每次访问来说是有益处的。
这种Optional就可以定义为implicitly unwrapping optionals,将问号(String?
)替换为叹号(String!
),之前的Optional就成为了implicitly unwrapping optionals
implicitly unwrapping optionals在这种情况下有用:在optional第一次定义时就能立即明确其有值,并可确信此之后一直有值。在Swift中,implicitly unwrapping optionals主要用于类初始化的时候。
implicitly unwrapping optionals其实就是一个普通的Optional,但是可以当作非Optional值来使用,并且不用在每次访问都时候都对其解包。接下来的例子将说明,在以explicit String的方式访问optional String 与 implicitly unwrapping optionals String时中的值时,在行为上有什么不同:
<pre><code>
let possibleString: String? = "An optional string."
let forcedString: String = possibleString! // requires an exclamation mark
<br />
let assumedString: String! = "An implicitly unwrapped optional string."
let implicitString: String = assumedString // no need for an exclamation mark
</code></pre>
可以认为implicitly unwrapping optionals是被许可在使用时自动解包的Optional。与每次使用Optional时将叹号!
放在名称后面不同的是:implicitly unwrapping optionals要求将叹号!
放在声明时的类型后面
注:如果访问无值的implicitly unwrapping optionals,将会引发运行时错误(runtime error),这与访问无值Optional时将叹号
!
放在名称后面一样,都会引发的运行时错误。
implicitly unwrapping optionals可以与普通的Optional一样,在if
语句中检查是否为有值:
<pre><code>
if assumedString != nil {
print(assumedString)
}
// prints "An implicitly unwrapped optional string."
</code></pre>
implicitly unwrapping optionals也可以用于Optional Binding,检查并解包其值:
<pre><code>
if let definiteString = assumedString {
print(definiteString)
}
// prints "An implicitly unwrapped optional string."
</code></pre>
注:如果变量在以后某刻可能为
nil
,就不要使用implicitly unwrapping optionals,而是使用普通的Optional类型,用于检查其是否有值。
错误处理 Error Handling
使用错误处理(Error Handling)应对在程序运行时遇到的突发的错误情况
相比于使用Optional可以在函数成功或失败时返回有值或无值,错误处理让你有机会判断引发错误的原因,如果有必要,还可以将错误继续传递到程序的其他部分去处理
当函数发生错误,就会抛出一个error,函数调用中可以捕获这个错误并进行适当处理
<pre><code>
func canThrowAnError() throws {
// this function may or may not throw an error
}
</code></pre>
声明函数时,在括号后紧跟throws
关键字。当调用一个可能抛出错误的函数时,必须在函数前使用try
关键字。
Swift会自动将error从其当前的作用域向外传递,直到遇到有catch
处理这个错误
<pre><code>
do {
try canThrowAnError()
// no error was thrown
} catch {
// an error was thrown
}
</code></pre>
do
语句创建了一个作用域,可以使错误传递给一个或多个catch
处理
下例将展示错误处理怎样响应不同情况的错误
<pre><code>
func makeASandwich() throws {
// ...
}
<br />
do {
try makeASandwich()
eatASandwich()
} catch Error.OutOfCleanDishes {
washDishes()
} catch Error.MissingIngredients(let ingredients) {
buyGroceries(ingredients)
}
</code></pre>
本例中,makeASandwich()
函数会在没有干净盘子或没有食材不全时抛出错误。因为makeASandwich()
声明为throws
,所以要使用try
来调用。将调研放倒do
语句里,有错误产生时将会被传递到对应的catch
语句中处理
如果没有错误发生,eatASandwich()
函数将被调用;如果抛出了Error.OutOfCleanDishes
错误,washDishes()
函数将被调用;如果抛出了Error.MissingIngredients
错误,buyGroceries(_:)
函数将被调用,同时在catch
中会得到缺失的食材数组[String]
以后会有单章来更详细地介绍throwing, catching, and propagating errors
断言 Assertions
有时,当特定的条件得不到满足时,程序将无法继续执行。碰到这种情况,就可以引发一个assertion来结束程序执行,使debug有机会去判断产生assertion原因(无效值或无值)。
Debugging with Assertions
Assertion就是在运行时检查其逻辑条件评估后是否为true
。从字面来讲,断言断定条件为true
。可以使用Assertion确保在运行其后的代码时,必要的条件已经得到满足。
如果在Debug时,代码引发了assertion,比如你正在使用XCode生成运行app,你就可以准确地找到无效状态的位置,并查询发生assertion时程序的状态。Assertion还允许你提供恰当的信息用以描述引发assertion的原因
使用全局函数assert(_:_:)
来描述一个断言。它有两个参数,其一是一个表达式用以评估其结果为true
或 false
,另一个是断言的描述信息,当表达式结果为false
时将会显示。
<pre><code>
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
</code></pre>
本例中,如果age >= 0
为true
,即:age
为非负数,就继续执行;如果age
是负数,就会引发断言,结束程序执行。
如果不想要断言描述信息,可以使用如下形式调用
<pre><code>
assert(age >= 0)
</code></pre>
When to Use Assertions
断言用于当条件有可能不成立,但是必须成立时程序才能继续执行的地方。使用断言的合适的情景:
- 传给自定义下标函数的下标值是否在正确范围内
- 传给函数一个无效值,导致不能完成其功能
- 当前Optional是
nil
,但是随后的代码需要其非nil
是才能执行成功
注:断言会导致程序终止,并且设计代码时不太可能出现的无效条件最好不要使用断言。尽管如此,在程序发布前的开发阶段,如果无效条件可能出现,断言也是发现无效条件的有效方法
网友评论