前言
本来之前计划是11月份开始学习Swift语言的,但是前段时间项目赶进度,加班厉害。所以计划没启动几天就搁置了,现在终于闲了下来,可以好好开始学习学习Swift了。Swift从苹果推出至今,已经出现了好几次比较大的版本更新,现在是Swift 3.0了,语法趋于稳定。而且Swift语言更简洁,更安全,更强大。它应该会越来越流行,最终取代OC作为iOS开发的第一语言。作为开发人员应当保持学习的激情,紧跟技术更新,不断学习积累。in one word,up or out.
元组
元组是OC中没有的一种新数据结构,它有点将数组和字典揉合的感觉,既像数组也像字典,使用起来极其灵活。
元组的初始化和使用都极其灵活,下面是些例子:
初始化时不申明元组类型,赋值时不申明元素名字。
此时则类似数组,可通过索引访问各个元素。但元组和数组不同的是,数组各项元素的类型必须一致,而元组不是,它的元素可以是任意类型。
let a = ("wang66", "wangpsd", true)
print("a---\(a)")
print("a---\(a.0)")
print("a---\(a.1)")
print("a---\(a.2)")
初始化时申明元组类型,但赋值时不申明元素名字:
此时仍类似数组。只不过申明元组类型(各元素类型)使其看起来更清晰。
let aa: (String, String, Bool) = ("wang66", "wangpsd", true)
print("aa---\(aa)")
print("aa---\(aa.0)")
print("aa---\(aa.1)")
print("aa---\(aa.2)")
初始化时不申明元组类型,但赋值时申明元素名字。
此时则类似字典,通过元素名字可访问其值。
let b = (name:"wang66", passwd:"wangpsd", isLogin:true)
print(b)
print(b.name)
print(b.passwd)
print(b.isLogin)
初始化时申明元组类型,且赋值时申明元素名字
let bb: (String, String, Bool) = (name:"wang", passwd:"wang", isLogin:true)
print(bb)
print(bb.0)
print(bb.1)
print(bb.2)
初始化时申明元素类型,且在元素类型里申明了元素名字。而且在赋值时带上了元素名字。
let bbb: (name:String, passwd:String, isLogin:Bool) = (name:"wang", passwd:"wang", isLogin:false)
print(bbb)
print(bbb.0)
print(bbb.passwd)
print(bbb.isLogin)
初始化时申明元素类型,且在元素类型里申明了元素名字。但在赋值时没有写元素名字。
此时,仍旧可以通过元素名字得到值。
let bbbb: (name:String, passwd:String, isLogin:Bool) = ("wang", "wang", false)
print(bbbb)
print(bbbb.name)
print(bbbb.passwd)
print(bbbb.isLogin)
还可以将元组当作一些变量/常量的合集来使用:
let (name, passwd, isLogin) = ("wang", "wang", true)
print(name)
print(passwd)
print(isLogin)
何处该用元组?
当你想让你的方法返回多个类型的多个值时,这是元组的最佳使用场景。如果返回的多个值类型是一致的,以往我们使返回类型为一个字典。但是若返回的多个值为不同的类型,以往我们可以将其封装为一个model
,而有些时候感觉就几个数据并没有必要封装为model
。在Swift中,元组可以完美地解决这个问题。
func getStudentInfo() -> (name: String, age: Int, isPass: Bool) {
let stuInfo = (name:self.stuName, age:self.stuAge, isPass: self.isPass)
return stuInfo
}
可选类型
可选类型(optionals),也是Swift的新特性。它表示** 可以有值,也可以没值;有值时为x,没值时便为nil。**
OC中也有nil,但是那只适用于对象,而Swift的可选类型却是适用于所有类型的,包括基本数据类型。
Swift之所以引进可选类型,主要是为了提高安全性。因为在代码中经常有些“未预期的变量为空”情况,在有些情况下很可能会程序crash掉,存在很大安全隐患。我们在写代码时为了避免出现这种安全问题,常常在代码中写入了了大量“防崩代码”,在调用方法前先判断调用者是否为空,在定义的方法内部判断参数是否为空等。
Swift就此做了努力,使这些可能为空的变量,在编译器编译时不予通过,将隐患扼杀在摇篮里。
下面先申明了一个字符串,但是没有初始化,然后打印该字符串。编译报错:❌
variable 'str' used before being initialized
。告诉我们str没有赋初值。
var str: String
print(str)
而我们将该字符串申明为可选类型后,便不会报错了。打印结果为nil。
var str: String?
print(str)
可选类型的拆包
Optional类型的值不能被直接使用,当需要用时要“拆包”,拆包有“显式拆包”和“隐式拆包”。
显式拆包:
所谓显式拆包,就是当变量用
?
申明未可选变量后。在使用变量时需要使用!
来拆包, 获得它的值。
var str: String? = "wang66"
print(str!)
但这样一点也不安全,假如该str申明后没有被赋初值就拆包的话,编译可以通过,但是运行时会报错:❌unexpectedly found nil while unwrapping an Optional value
。
所以我们应当用if语句判断str,不为空时才进行拆包取值。注意:
!=
两侧都必须有空格,不然编译会报错:❌'=' must have consistent whitespace on both sides
。
var str: String?
if str != nil {
print(str!)
}else{
print("str is nil")
}
可选绑定:
像上面那样看起来不错了,但是也不完美。因为你在每次使用该变量时都被要求进行拆包。(虽然编译不会报错,但没有拆包,拿不到正确的值。会打印:
Optional("1234")
)。这是何其麻烦。
var str: String?
str = "1234"
if str != nil {
print(str!)
print("str=\(str!)")
}else{
print("str is nil")
}
为了避免每次使用变量时都要拆包,Swift引进了“可选绑定”:在if后面将可选变量赋给一个临时量,Swift会自动检测其是否包含值。若包含,则隐式拆包。那我们在后续便可以直接使用这个临时量了。
这个临时量既可以时常量let
,也可以是变量var
。
var str: String?
str = "1234"
if let tempStr = str{ // 将str赋给一个临时量,Swift会自动检测其是否包含值。若包含,则隐式拆包。
print(tempStr)
}
隐式可选类型的隐式拆包:
上面我们说了在申明变量/常量时,在类型后面加
?
表明它是个可选变量/常量。其实,Swift中还可以在类型后面加!
,这代表什么呢?这代表它是个“隐式可选类型”。它是个可选类型,但是是隐式的。何谓隐式?就是我们在使用这个变量时就已确定它被初始化,不为nil了。我们每次直接使用变量,编译器会自动帮我们拆包。
let str: String! = "wang66"
print(str)
除非你真的确定你的变量已被初始化,不为nil。否则不要轻易使用“隐式拆包”,它也是有隐患的。
下面的代码也会报上面出现的错误:❌unexpectedly found nil while unwrapping an Optional value
。
str被置为nil了,然后编译器在自动拆包时,便蒙逼crash了。
var str: String! = "wang66"
str = nil
print(str)
可选链式调用:
首先链式调用我们都知道,但是因为在Swift中有可选类型,这就导致了如果链式调用的某个环节,某个属性值为nil,某个方法返回值为nil,如果继续往链后面调用的话,会出问题的。所以为了避免这个问题,Swift是这样处理的。
class UserModel: NSObject {
var userName: String?
var address: String?
var book: BookModel?
func buy() {
print("userName:\(userName), address:\(address), bookName:\(book?.bookName), price:\(book?.price)")
}
}
class BookModel: NSObject {
var bookName: String?
var price: Double?
func bookDesc() {
print("bookName:\(bookName), price:\(price)")
}
}
上面定义了两个类UserModel
和BookModel
。两个类中的属性均为可选的,即有可能是空的。UserModel
中的属性之一是BookModel
类型的book
。我们创建UserModel
的实例user
来通过链式语法来读写book
属性。Xcode会自动帮我们在链中可能为nil
的某实例后自动加上?
作为处理,当非nil
时,沿着链继续往后调用;若为nil
,则返回nil
,链式调用终止。
let user = UserModel()
user.book = BookModel()
user.book?.bookName = "wanger"
user.book?.price = 41.0
user.book?.bookDesc()
print("bookName:\(user.book?.bookName!)")
for-in循环
当我在swift中写出一个经典的for循环时,编译器报错了❌,而且是两个错误:
1.'++' is unavailable: it has been removed in Swift 3
; 自增语法在swift3上废除了。
2.C-style for statement has been removed in Swift 3
C风格的循环写法在Swift3上废除了。
let arr = ["wang11", "wang22", "wang33", "wang44"]
for var i=0; i<10; i++
{
print(arr[i])
}
看来循环语句不能这么写了。Swfit语句中只有for-in循环,但它完全够用了,可以取代经典的C写法。
let arr = ["wang11", "wang22", "wang33", "wang44"]
for item in arr {
print(item)
}
之所以说它可以取代C写法的for循环,是因为还可以这么写。
(0...arr.count-1)
叫“闭区间操作符”,index
的值在for循环执行的过程中,从闭区间的第一位取值,直到闭区间最后一位时循环结束。
let arr = ["wang11", "wang22", "wang33", "wang44"]
for index in (0...arr.count-1) {
print(arr[index])
}
(0...x)
之所以叫闭区间,是因为首尾值都包括在内。Swift也提供了“半开半闭区间”(0..<x)
,是前闭后开的。还是上面的例子:
let arr = ["wang11", "wang22", "wang33", "wang44"]
for index in (0..<arr.count) {
print(arr[index])
}
遍历字典的情况:将遍历出的每项字典元素赋给一个临时元组,后续使用时从元组中取值。
let dict = ["wang11":24, "wang22":23, "wang33":25, "wang44":26]
for (dictKey, dictValue) in dict {
print("dictKey:\(dictKey)----dictValue:\(dictValue)")
}