文章目录
引言
本文旨在帮助开发者快速从OC开发过渡到Swift开发,挑选了一些比较浅显的但是比较常用的Swift语法特性,在介绍的过程中,通常会拿OC中的语言特性作比较,让大家更好的注意到Swift的不同。
另外需要说明的是,笔者也仅仅是刚刚接触Swift不久,如果有说的不对的地方,还望指正,这里贴出本文所参考的 Swift中文翻译地址,方便大家可以深入了解Swift。
本文发布时,Swift 刚出到 4.1 版本,目前已经发布 5.2 版本,文中的部分内容可能已经不适用,后期会给予修正。
Swift简介
Swift 是一门开发 iOS、macOC、watchOS 和 tvOS 应用的新语言,在初期,为了让 OC 开发者快速的过渡到 Swift 开发,Swift 继承了很多 C、OC 的语法特性,但是随着 Swift 的不断完善,已经慢慢自称一体,不仅保留了 OC 的很多语言特性,还借鉴了很多语言,如 C#、Java、Python 等。
Swift 包含了 C 和 OC 上所有的基础数据类型:Int、Double、Float、Bool、String,集合类型:Array、Set 和 Dictionary,除此之外,Swift 增加了 OC 中没有的高阶数据类型如元组(Tuple),元组方便我们接收或传递一组数据,或返回多个值而不必使用结构体、类等。
另外,Swift 新增了可选(Optional)类型,可选表示有值或者没有值,需要注意的是,Swift 中,不同类型的数据类型是不能够相互操作的,如 Int?和 Int 属于不同类型,又如 Int 和 Float。
Swift基础篇
常量let和变量var
常量一旦设定就不可改变,变量则值可变。常量使用let
来声明,变量使用var
表示。
let PI = 3.1415
var Str = "string"
Swift 可以根据赋值推断出类型,你也可以使用类型标注(type annotation)来指定类型
let PI: Float = 3.1415
var Str: String = "string"
一般来说声明常量或变量形式如下:
let/var name: type = value
输出print
你可以使用print
函数输出常量和变量,输完换行。Swift 取消了旧版的println
输出方法,另外,如果你想使用OC中的NSLog也是可以的。print(, separator: , terminator: )
中,将 terminator 参数设为空字符""
则可以不进行换行。
Swift使用字符串插值(string interpolation)的方式将常量或变量当作占位符加入到长字符串中,我们可以借此拼接长字符。
let value1 = "123"
var value2 = 345
print("value1 = \(value1) , value2 = \(value2)")
// 输出: value1 = 123 , value2 = 345
数据类型(布尔值、数组、字典、元组、可选类型)
- 布尔值
和 OC 不同,Swift 中的布尔值使用true
和false
表示真、假
let boolValue = true
if boolValue {
print("value is true")
}
这里需要说明的是,Swift 不能像 OC 中那样,数值为0即表示布尔值NO
,非0即为YES
,因此下面OC代码可以通过
float value = 0.001;
if (value) {
NSLog(@"value:%f",value);
}
// 输出: value:0.001000
而在Swift中会编译报错
let value = 0.001
if boolValue {
print("value is true")
}
Swift 在判断语句中必须采用布尔值
let value1 = 1.1
let value2 = 2.0
if value1 > value2 {
print("value1:\(value1) 大于 value2:\(value2)")
}else{
print("value1:\(value1) 小于 value2:\(value2)")
}
// 输出: value1:1.1 小于 value2:2.0
- 数组(array)
数组是一种泛型应用,关于泛型,后面会说到。
数组有多种创建方式
var list1:Array = [1,2,3] // 标准创建,元素类型由系统推断
var list2:Array<Int> = [1,2,3] // 指定类型,一种泛型应用
var list3 = [1,2,3] // 由系统推断类型
var list4:[Int] = [1,2,3] // 简写Array,并指定元素
var list5 = [Int]([1,2,3]) // [Int]()则创建空数组
var list6 = Array([1,2,3]) // 等同于Array.init([1,2,3])
var list7 = Array.init(repeating: 1, count: 3) // 重复3次元素1,数组类型由元素推断
需要说明的是:Swift 中并没有可变不可变两种数组类型之分,不可变类型可以通过let
来声明,var
声明的数组都是可变的,类似的,字典类型也是如此。
数组的一些常用操作
var a = [Float]()
a.append(1.1) // 添加元素
a += [2,5] // 拼接其他数组
a[1...2] = [11,22] // 修改元素, '...'表示范围range
a.insert(33, at: a.count) // 插入元素,此处插在了数组的末尾
a.remove(at: a.count-1) // 移除指定位置的,此处相当于 a.removeLast()
a.removeFirst() // 移除第一个
// 遍历数组
for value in a {
print("index=\(a.index(of: value)!),element=\(value)")
}
for (index,value) in a.enumerated() {
// (,)表示元组类型
print("index=\(index),element=\(value)")
}
- 字典(Dictionary)
字典的创建和数组类似,可以由系统推断出元素类型,也可以指定元素类型。和 OC 不同的是,Swift 创建不使用 @{}
语法糖创建,而是使用和数组一样的 []
形式,和数组不同的是,[]
中需要 :
分隔,左边是 key 的类型,右边是 value 类型,下面演示其中一种创建方式。
// [Int:String]为简写,其中Int为key的类型,String为value类型
var d:[Int:String] = [200:"success",404:"not found"]
字典的一些常用操作
d[200] // 取值
d[500] = "internal server error" // 添加
// 遍历字典
for key in d.keys {
// 遍历key
print("key=\(key)")
}
for value in d.values {
// 遍历value
print("value=\(value)")
}
for (key,value) in d {
// 遍历元组
print("key=\(key),value=\(value)")
}
- 元组
元组(tuples)把多个值组合成一个复合值。元组内的值可以是任意类型,这一点和数组不同,它不要求元素相同类型,这里的元组和 Python 中的元组功能类似。
Swift 的元组使用圆括号()
创建,不能省略,这点和Python不同。
let size = (100,200)
// 输出 100 200
print(size.0,size.1)
// 也可以指定名称
let httpError = (code:404,reason:"Not Found")
print(httpError.code,httpError.reason) // httpError.code 和 httpError.0 是一样的
// 输出 404 Not Found
应用举例:实现交换
var (v1,v2) = (10,20)
print("v1:\(v1),v2:\(v2)")
// 输出 v1:10,v2:20
(v1,v2) = (v2,v1)
print("v1:\(v1),v2:\(v2)")
// 输出 v1:20,v2:10
应用举例:函数多参数返回,在数组和字典中的遍历过程中已经有体现
需要注意的是:元组在临时组织值的时候很有用,但是并不适合创建复杂的数据结构。如果你的数据结构并不是临时使用,请使用类或者结构体而不是元组。
- 可选类型
可选类型:有值 或者 没有值
我们先来看下系统方法产出的可选类型。Int 类型有一种构造器,将一个 String 值转换为一个 Int 值,但是,并不是所有的字符串都可以转换为一个整数。如 “123” 可以被转换为数字123,而字符串"hello, world"则不可以。
let possibleNumber = "123"
let convertedNumber = Int(possibleNumber)
// 当你敲convertedNumber时,可以看到类型"Int?"类型
这时因为这个构造器可能会失败,所以它返回一个可选类型 Int?
,而不是 Int
(注意,这两种属于不同类型),?
表示包含的值可能有值,也可能没有值,当我们需要使用 Int 类型时,需要转换为 Int 才可以。
声明一个可选类型
使用 ?
来声明一个可选类型
var serverResponseCode: Int? = 404
// serverResponseCode 包含一个可选的 Int 值 404
print("\(serverResponseCode!)") // 这里的'!'表示强解
// 输出 404
serverResponseCode = nil
// serverResponseCode 现在不包含值
如果你声明一个可选常量或者变量却没有赋值,他们会自动被设置为nil,就像下面这样:
var surveyAnswer: String?
// surveyAnswer 被自动设置为 nil
这时如果你想要使用 !
强解 surveyAnswer 会发生错误,所以在确定一个可选值时,务必保证可选值不为 nil。
可选绑定
我们可以使用可选绑定(optional binding)来判断可选类型是否包含值,如果包含值就把值赋值给一个临时常量或者变量。通常会出现在 if 和 while 语句中,这条语句不仅可以用来判断可选类型中是否有值,同时可以将可选类型中的值赋值给一个常量或者变量。
let possibleNumber = "123"
if let actualNumber = Int(possibleNumber) {
print("\'\(possibleNumber)\' has an integer value of \(actualNumber)")
} else {
print("\'\(possibleNumber)\' could not be converted to an integer")
}
// 输出 "'123' has an integer value of 123"
因为 Int(possibleNumber) 包含来一个值,因此进入 if 的方法体,并会将该值赋值给常量 actualNumber。
隐式解析可选类型
可选类型除了使用可选绑定来解析值,我们也可以使用 !
来解析值,但是可选此时不可出现 nil 的情况,否则会出现错误。
有时候,第一次被赋值之后,可以确定一个可选类型总会有值。在这种情况下,每次都要判断和解析可选值是非常低效的,因为可以确定它总会有值。
这种类型的可选状态被定义为隐式解析可选类型,我们可以将 ?
换成 !
来声明一个隐式解析可选类型,这种情况下,我们无需手动使用 !
来解析可选值,系统会帮我们完整可选类型的解析。
let possibleString: String? = "An optional string."
let forcedString: String = possibleString! // 需要感叹号来获取值
// 表示该成员变量必定有值
let assumedString: String! = "An implicitly unwrapped optional string."
let implicitString: String = assumedString // 不需要感叹号
需要注意的是:和普通可选类型一样,如果你在隐式解析可选类型没有值的时候尝试取值,同样会触发运行时错误。因此,如果一个变量之后可能变为 nil 的话,请不要使用隐式解析可选类型,否则请使用普通可选类型。
可选类型在开发过程中经常出现,大家需要好好理解,正确使用使用 ?
和 !
。
- Any、AnyObject和AnyClass
Any : 表示任意类型,Any? 还包括了 nil
AnyObject : 代表任务 class 类型,无论 class 是否谁的子类又或者是基类
AnyClass : AnyObject的别名,和AnyObject一样
几种运算符
这里提及一些和 OC 中不一样的几种运算符。
- 区间运算符
区间运算符通常用于整型或者范围。区间运算符有两种: ..<
和 ...
,前者为开区间 CountableRange,后者为闭合区间 ClosedRange。
// 闭区间运算符
for i in 0...9 {
print(i)
}
// 半开区间运算符
let names = ["Anna", "Alex", "Brian", "Jack"]
for i in 0..<names.count {
print("第 \(i + 1) 个人叫 \(names[i])")
}
// 第 1 个人叫 Anna
// 第 2 个人叫 Alex
// 第 3 个人叫 Brian
// 第 4 个人叫 Jack
// 单侧区间
for name in names[2...] {
print(name)
}
// Brian
// Jack
for name in names[...2] {
print(name)
}
// Anna
// Alex
// Brian
let range = ...5
range.contains(7) // false
range.contains(-1) // true
- 空合运算符
空合运算符(a ?? b)将对可选类型 a 进行判断,如果 a 包含一个值就进行解封,否则就返回一个默认值 b。注:表达式 a 必须是可选类型,而 b 的类型必须和 a 存储值的类型保持一致。
let bValue = "default value" // 作为默认值
var aValue : String? // 默认值为 nil
let result = aValue ?? bValue
print(result)
// 输出 default value
- 溢出运算符
在默认情况下,当向一个整数赋值超过它的容量时,Swift 会报错。我们可以使用 max
或 min
访问整形的最大或最小值
var maxInt = Int8.max
var minInt = Int8.min
print("Int8.max:\(maxInt), Int8.min:\(minInt)")
// 输出 Int.max:127, Int.min:-128
如果此时我们尝试给 maxInt 加1或者给 minInt 减1都会错误
maxInt += 1 // 编译错误
minInt -= 1 // 编译错误
我们可以使用溢出运算符来解决这种上溢或下溢现象
minInt = minInt &- 1 // 相同的有 &- &* &/
print("\(minInt)")
// 输出 127
Int8 型整数能容纳的最小值是 -128,以二进制表示即 10000000。当使用溢出减法运算符对其进行减 1 运算时,符号位被翻转,得到二进制数值 01111111,也就是十进制数值的 127,这个值也是 Int8 型整数所能容纳的最大值。
另外,在新版 Swift 中,++
和 --
运算符被取消,因此 i++
这种形式的累加需要换成 i += 1
这种形式。
控制语句
Swift 提供了多种控制流语句,包括 while 循环、if、guard、switch、跳转break、continue 等等。在 Swift 中,for-in 循环可以更简单的遍历数组、字段、区间、字符等序列类型,不同于 C 和 OC,Swift 在新版本中取消了 C 的 for 条件循环,即 for var i=0;i<a;i++
这种形式。值得一提的是,Swift中的 switch 语句比 C 中更加强大,case 可以匹配不同的模式,包括范围匹配,元组和特定类型匹配,甚至是使用 where 来描述更多约束的情况。详情可以看官网翻译的控制流。
我们这里介绍提前退出 guard。
像 if 语句一样,guard 的执行取决于一个表达式的布尔值。当 guard 要求条件的为真时,可以继续执行 guard 语句之后的代码,否则提前返回结束。和 if 不同的是,guard通常只有一个 else 从句。
func greet(_ name:String) {
guard name == "LOLITA0164" else {
print("hello! \(name)")
return
}
print("\(name)")
}
greet("xiao ming") // 输出 hello! xiao ming
greet("LOLITA0164") // 输出 LOLITA0164
我们看到,当输入"xiao ming" 时,条件为 false,输出 “hello! xiao ming” 后返回退出,当输入 “LOLITA0164” 时,条件为真,继续执行 guard 后面当语句,输出 “LOLITA0164”。
因此 guard 可以用来控制语句不满足条件时提前结束。
函数和闭包
- 函数
Swift 的函数参数和返回值非常灵活。参数可以是的无参、多参、默认参、可变参、甚至一些高级语言中的输入输出参数,参数类型也非常多,甚至包括另一个函数,另外Swift 函数支持嵌套参数。在返回类型上,可以是无返回类型,多参数返回、甚至是一个函数。
Swift 的函数使用 func
声明一个函数,形式为:
func name(parameters) -> return type {
function body
}
在上一个介绍中,已经涉及到了函数创建和使用 greet
,下面介绍一下常用的函数形式。
// 无参数函数,有返回值
func sayHello() -> String {
return "hello, world"
}
// 多参数函数,无返回值( -> Void 可以省略 )
func personInfo(name: String, age: Int) -> Void {
// function body
}
// 多重返回值,这里返回一个元组
func minMax(array: [Int]) -> (min: Int, max: Int) {
return (array.min()!, array.max()!)
}
// 可选返回类型,‘_’ 表示忽略参数标签
func max(_ array: [Int]) -> Int? {
return array.max()
}
// 指定参数标签(参数说明,和参数名以空格分隔),区别于参数名,默认参数名就是外部的标签名
func someFunction