Swift备忘录
Swift 备忘录 2015-4
一、简介
1、Swift 语言由苹果公司在2010年7月开始设计,在 2014 年6月推出,在 2015 年 12 月 3 日开源
2、特点(官方):
(1)苹果宣称 Swift 的特点是:快速、现代、安全、互动,而且明显优于 Objective-C 语言
(2)可以使用现有的 Cocoa 和 Cocoa Touch 框架
(3)Swift 取消了 Objective-C 的指针及其他不安全访问的使用
(4)舍弃 Objective-C 早期应用 Smalltalk 的语法,全面改为句点表示法
(5)提供了类似 Java 的名字空间(namespace)、泛型(generic)、运算对象重载(operator overloading)
(6)Swift 被简单的形容为 “没有 C 的 Objective-C”(Objective-C without the C)
3、强语法规范以及一些注意点
(1)空格的使用(对比OC)
(2)数据类型的异常严格的要求
(3)不用导入头文件,使用pod时例外
二、常量变量
1、 let :定义常量,一经赋值不允许再修改
var: 定义变量,赋值之后仍然可以修改
使用技巧:优先选择 let 定义常量/变量, Xcode7.0以后如果变量没有修改,会给出警告,提示使用let代替var
2、未赋值的常量有一次赋值的机会,例:
let x1: Int
x1 = 30
3、关于自动推导
(1)swift 可以根据右边的类型准确推导出变量的类型,例如 var num = “张三” ,num就是一个字符串类型
(2)开发中一般不用指定变量类型
(3)如果要指定,跟在后面,例如 var num : String = “张三”
4、option + 左键点击 的使用:查看变量的类型
5、没有隐式转换,不同数据类型间的运算,必须统一数据类型(显式强转换),例:
let x2 = 100
let y2 = 10.5
let num1 = Double(x2) + y2
let num2 = x2 + Int(y2)
三、Optional 可选类型
1、定义:swift一大特色,表示一个变量的值可以是nil,也可以是一个指定的值。
2、表示方式:在类型后面加? 例如: var num : Int ?
3、被设置成可选类型的变量,默认值是nil
4、被设置成可选类型的常量,没有默认值,其意义也不大,例:
let url = NSURL(string: "http://www.baidu.com")
let num: Int? = 10
四、关于可选项的解包(判断是否为nil)
1、不可参与运算&&可以打印:如果 Optional 值是 nil,不允许参与计算,只有解包(unwrap)后才能参与计算,但是解包前是可以打印的
2、解包方式:
(1)在变量后添加一个 ! 可以强行解包
注意:必须要确保解包后的值不是 nil,否则会报错
例(错误提示):unexpectedly found nil while unwrapping an Optional value
(2)简单的 if 判断,但还是需要使用到强行解包
if url != nil {
let request = NSURLRequest(URL: url!)
}
(3)使用 if let 、guard let等特殊处理方式
(4)可选链,使用一个 ?给对象进行 “弱” 解包,有值则正常返回,无值则返回nil
(5)空合并运算符??
3、避免强解包造成崩溃、不使用强制解包————> 详解
(1)if let 判断
例:如果可选项num为空,则不执行if分支的代码,也就不会崩溃
if let n = num {
let r = n + 10
}
(2)where子句
语义:如果判断的条件不止一个,判断的条件具备从属关系,又要避免多层的(难看的)嵌套if,可以使用where
if let 不能与 &&、|| 等条件判断方式一同使用,如果要增加条件,可以使用 where 子句(where 子句没有智能提示)
例:
if let u = url where u.host == "www.baidu.com" {
let request = NSURLRequest(URL: u)
}
(3) guard 判断,与 if let 语义相反,Swift 2.0 推出 (在真正的逻辑代码部分,可以省掉一层嵌套)
语义:如果变量为nil 则执行 分支中的代码 。
例:
let oName: String? = "张三"
let oNum: Int? = 18
guard let name = oName else {
return
}
guard let num = oNum else {
return
}
// 代码如果能执行至此,name & num 都是一定不为nil的,此处也没有if let中的嵌套(少了一层),随便写~~
print(name)
print(num)
(4)空合并运算符 ??
语义:如果为nil,则使用??后面的值代替,简化了代码,例如:
let num: Int? = nil
let r1 = (num ?? 0) + 10
print(r1)
(5)多个可选类型判断(避免多个可选项为nil)
方法:可以使用 “ ,” 同时判断多个可选类型是否为空
let oName: String? = "张三"
let oNo: Int? = 100
if let name = oName, let no = oNo {
print("姓名:" + name + " 学号: " + String(no))
}
(6)可选链 “ ?”的使用,对比 “ !”
语义:如当对象为可选类型时,对其属性的访问,要先解包,但如果使用!,有可能因为nil造成崩溃,这时可以使用?,如果对象存在,则解包调用,如果对象为nil则不进行后续操作。
例:
var p : person?
let name = (p?.name) ?? ""
print(name)
五、控制流
1、if 的注意点:
(1)没有OC中非零及真的概念
(2)明确显式的指出 true 或 false 进行逻辑判断
(3)()可以省略,{}不可以省略
2、三目运算符与OC相同
3、switch(变化很大)
(1)switch 不再局限于整数, 可以针对任意数据类型进行判断
示例:
let score = "优"
switch score {
case "优":
let name = "学生"
print(name + "80~100分")
case "良": print("70~80分")
case "中": print("60~70分")
case "差": print("不及格")
default: break
}
(2)可以省略 break
(3)每一个 case后面必须有可以执行的语句,不能什么都不写
(4)要保证处理所有可能的情况,不然编译器直接报错,不处理的条件可以放在 default (不可省)分支中
(5)每一个 case 中定义的变量仅在当前 case 中有效,而 OC 中需要使用 {},swift可以省略
(6)同样有穿透,使用关键字 fallthrough,只穿透一层, 而且case语句中不能定义变量或常量
(7)switch 中同样能够赋值和使用 where 子句
示例:
let point = CGPoint(x: 10, y: 10)
switch point {
case let p where p.x == 0 && p.y == 0:
print("中心点")
case let p where p.x == 0:
print("Y轴")
case let p where p.y == 0:
print("X轴")
case let p where abs(p.x) == abs(p.y):
print("对角线")
default:
print("其他")
}
(8)如果只希望进行条件判断,赋值部分可以省略,使用 _ , 表示“不关心”
示例:
switch score {
case _ where score > 80: print("优")
case _ where score > 60: print("及格")
default: print("其他")
}
六、循环
1、for 循环
(1)仿OC写法 ,()可以省略
var sum = 0
for var i = 0; i < 10; i++ {
sum += i
}
print(sum)
(2)for - in 写法
语义 :在一个范围(或集合中)循环遍历出每个个体 ,例:
- 范围 0..< 10 表示从0到9 , i 就是 0、1、2 ……9 的个体,一个循环10次,,对比0...10
sum = 0
for i in 0..<10 {
sum += i
}
print(sum)
- 遍历子控件集合,打印每一个子控件
for myView in view.subviews {
print(myView)
}
(3)for - in 省略下标写法
语义:忽略_ 对应位置的值
for _ in 0...10 {
print("hello")
}
2、do - while 循环
(1)swift2.0以后把do-while 改成了repeat- while
例:
var index = 3
repeat {
index--
print(index)
} while index > 0
3、while 循环,和OC一样,可以省略()
七、字符串
1、结构体类型 Sting
(1)let 定义不可变字符串
(2)var 定义可变字符串
(3)定义空字符串:
var a: String = String()
2、与NSString比较:
(1)String 是一个结构体,性能更高,具有了绝大多数 NSString 的功能
(2)String 支持直接遍历
(3)NSString 是一个 OC 对象,性能略差
(4)Swift 提供了 String 和 NSString 之间的无缝转换
3、遍历字符串(characters属性)
示例:
for s in str.characters {
print(s)
}
4、字符串长度
(1)返回以字节为单位的字符串长度,一个中文占 3 个字节,根据编码方式
let len1 = str.lengthOfBytesUsingEncoding(NSUTF8StringEncoding)
(2) 返回实际字符的个数
let len2 = str.characters.count
(3) 返回 utf8 编码长度
let len3 = str.utf8.count
5、字符串拼接
(1)直接在 "" 中使用 \(变量名) 的方式可以快速拼接字符串
let str1 = "Hello"
let str2 = "World"
let i = 32
str = "\(i) 个 " + str1 + " " + str2
(2)可选项的拼接,如果变量是可选项,拼接的结果中会有 Optional字符,使用空合并运算符
let str1 = "Hello"
let str2 = "World"
let i: Int? = 32
str = "\(i ?? 0) 个 " + str1 + " " + str2
(3)格式化字符串
在实际开发中,如果需要指定字符串格式,可以使用 String(format:...) 的方式
let h = 8
let m = 23
let s = 9
let timeString = String(format: "%02d:%02d:%02d", arguments: [h, m, s])
let timeStr = String(format: "%02d:%02d:%02d", h, m, s)
6、截取字符串(复杂一些)
关键词 : startIndex、endIndex、startIndex.advancedBy、substringToIndex、substringWithRange
例:
首先定义常量 : let str = "hello"
(1)占位符 + substring 方式
//”hel"只是一个占位字符串,可以随便写, startIndex 是光标的开始索引
let startIndex = "hel".startIndex
let endIndex = "hel".endIndex
//从起点开始一直截取到末尾
let result = str.substringFromIndex(startIndex)
//截取到结束位数
let result1 = str.substringToIndex(endIndex)
(2) advancedBy + range 方式
//advancedBy 如果传入的是正数则是往后加多少为
let strStartIndex = str.startIndex.advancedBy(1)
let strEndIndex = str.endIndex.advancedBy(-1)
let result2 = str.substringWithRange(strStartIndex..<strEndIndex)
(3)转成OC + range 方式
let result3 = (str as NSString).substringWithRange(NSMakeRange(1, str.characters.count-2))
let.result4=(str as NSString).substringWithRange(NSMakeRange(2, 3))
八、数组
1、定义、获取元素与OC相同
例:
let numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
let num1 = numbers[0]
2、let 、var 区分可变和不可变数组
3、遍历使用 for - in ,例:
for num in numbers {
print(num)
}
4、追加元素、删除元素
array1.append(“张三”)
array1.removeAtIndex(3)
array1.removeAll()
5、数组的定义和实例化
(1)使用 : 可以只定义数组的类型 , 例:
var array: [String]
(2)实例化之前不允许添加值
(3)使用 [类型]() 可以实例化一个空的数组 ,例:
array = [String]()
array.append(“老王”)
6、数组的类型
(1)如果初始化时,所有内容类型一致,则数组中保存的是该类型的内容,后续添加的元素必须类型一致
(2)如果初始化时,所有内容类型不一致,则数组中保存的是 NSObject
(3)swift 中 数字可以直接加入集合中,不用包成NSNumber
(4)swift 中 将结构体添加到集合中,还是需要包成NSValue,例:
array.append(NSValue(CGPoint: CGPoint(x: 10, y: 10)))
7、数组的合并
(1)必须是相同类型的数组才能够合并
(2)开发中,通常数组中保存的对象类型都是一样的!
8、内存分配
(1)如果向数组中追加元素,超过了容量,会直接在现有容量基础上 * 2,例:
var list = [Int]()
for i in 0...16 {
list.append(i)
print("添加 \(i) 容量 \(list.capacity)")
}
一个奇怪的现象:添加第一个元素时,数组容量为 2
(2)数组移除元素后,保持容量
九、字典
1、定义字典
(1)使用 [ ] : var dict = ["name": "zhangsan", "age": 18]
(2)var dict : [String : NSObject] = [String : NSObject]( )
dict[key] = value
2、最常用的类型
(1)[String : NSObject]
(2)AnyObject 的范围要比NSObject大
3、赋值
(1)赋值直接使用 dict[key] = value 格式
(2)如果 key 不存在,会设置新值
(3)如果 key 存在,会覆盖现有值
4、遍历
(1)使用(key,value)+ for-in 的方式,key,value可以随便写
(2)前面的是 key,后面的是 value
例:
for (k, v) in dict {
print("\(k) ~~~ \(v)")
}
5、增删改,例:
(1)增、改:如果key不存在,则进行添加,如果key存在,则进行修改
var dic1 = ["name": “老王”,”age": 18]
dic1["sex"] = "男"
dic1["name"] = “老李”
(2)删
dic1["age"] = nil
dic1.removeValueForKey("name")
(3)改
dic1.updateValue(28, forKey: "age")
6、合并字典
(1)如果 key 不存在,会建立新值,否则会覆盖现有值
var dict1 = [String: NSObject]()
dict1[“nickname"] = "大老虎"
dict[“age"] = 100
for (k, v) in dict1 {
dict[k] = v
}
print(dict)
十、函数
1、函数的定义
(1)格式 func 函数名(行参列表) -> 返回值 {代码实现}
(2)调用 let result = 函数名(值1, 参数2: 值2...)
2、没有返回值的函数,一共有三种写法
(1)省略
(2)—> ()
(3) —>Void
3、外部参数
(1)在形参名前再增加一个外部参数名,能够方便调用人员更好地理解函数的语义
(2)格式:func 函数名(外部参数名 形式参数名: 形式参数类型) -> 返回值类型 { // 代码实现 }
例:
func sum1(num1 a: Int, num2 b: Int) -> Int {
return a + b
}
sum1(num1: 10, num2: 20)
(3)Swift 2.0 中,默认第一个参数名省略
(4)闭包中也有外部参数
4、swift 可以在函数中定义函数,函数也可以作为其他函数的参数
十一、闭包
1、定义:闭包类似于 OC 中的 Block
(1)预先定义好的代码块
(2)在需要时执行
(3)可以像变量一样当作参数传递
(4)可以像函数一样有返回值
(5)包含 self 时需要注意循环引用
2、作用:与 OC 中的 Block 类似,闭包主要用于异步操作执行完成后的代码回调,网络访问结果以参数的形式传递给调用方
3、格式:闭包 = { (行参) -> 返回值 in // 代码实现 },例:
let sumFunc = { (num1 x: Int, num2 y: Int) -> Int in
return x + y
}
sumFunc(num1: 10, num2: 20)
最简单的闭包,直接就是 闭包 = {代码实现 }
4、尾随闭包:函数的最后一个参数是一个闭包,则小括号可以提前关闭,闭包放在小括号外面
5、 如果函数的参数只有一个闭包,函数的小括号可以省略,例:
loadData { (result:String) -> () in
print(result)
}
6、闭包回调简单应用示例
(1) override func viewDidLoad() {
super.viewDidLoad()
// 调用带有闭包作为参数的函数(尾随闭包效果)
loadData() { (result) -> () in
print(result)
}
}
(2) // 函数需要传入一个闭包 闭包的类型是有参数无返回值得闭包
func loadData(callbackClosure: (result: String) -> ()) {
dispatch_async(dispatch_get_global_queue(0, 0)) { () -> Void in
// 模拟耗时操作
NSThread.sleepForTimeInterval(2)
let result = "办证:137xxxx"
dispatch_async(dispatch_get_main_queue(), { () -> Void in
callbackClosure(result: result)
})
}
}
十二、闭包的循环引用
1、原因,类似于OC中的block:
<1> OC中
(1)对象A 有一个 block 的属性, self 通过调用A的方法,将定义好的block 传递给对象A ,说明对象A 引用了这个block
(2)对象A 又作为 self 的一个属性, 说明 self 引用了对象A
(3)block 中出现 self , 说明这个block 引用了 当前 self
(4)到此 : 引用循环形成
<2> swift中(最简单的循环引用)
(1)闭包中 出现self , 说明闭包持有(引用)了self
(2)self 中 定义了这个闭包,说明self 引用了这个闭包
2、示例
(1)“全局”的闭包变量
var closure: (()->())?
(2)给闭包赋值,并且在闭包中引用了self
override func viewDidLoad() {
super.viewDidLoad()
closure = {
dispatch_async(dispatch_get_global_queue(0, 0), { () -> Void in
NSThread.sleepForTimeInterval(2)
dispatch_async(dispatch_get_main_queue(), { () -> Void in
print(self.view)
})
})
}
closure?()
}
(3)循环形成
3、解决
<1> OC中
(1)使用弱引用,破除环
(2)iOS5.0 推出:__weak typeof(self) weakSelf = self;
(3)iOS4.0 推出:__unsafe_unretained typeof(self) weakSelf = self;
(4)区别:__unsafe_unretained 如果当前对象self销毁后,weakSelf执行的地址不变,而这个地址对应对象已经销毁了,所以再去访问该对象就崩溃,形成野指针了,相当于assign。然而前者在self销毁后,会自动把weakSelf的地址设置成nil,所以不会有野指针
<2> swift中
(1)仿OC写法:
weak var weakSelf = self
(2)swift写法:
[weak self] 相当于 之前__weak 对象销毁后 self指向的地址为nil
[unowned self] 相当于 之前的 __unsafe_retain 对象销毁后 self执行的地址不变
closure = { [unowned self] in
// 判断weakSelf是否有值,如果有值则执行,否则直接返回nil
dispatch_async(dispatch_get_global_queue(0, 0), { () -> Void in
NSThread.sleepForTimeInterval(2)
dispatch_async(dispatch_get_main_queue(), { () -> Void in
print(self.view)
})
})
}
closure?()
}
(2)析构函数,对象释放后会执行这个函数,类比OC的dealloc
deinit {
// 移除通知
// 移除kvo
// 移除NSTimer
print("ViewController over")
}
十三、构造函数
1、定义:是一种特殊的函数,主要用来:
(1)在创建对象时初始化对象
(2)为对象成员变量设置初始值
2、和OC的对比:
(1)在 OC 中的构造函数是 initWithXXX,在 Swift 中由于支持函数重载,所有的构造函数都是 init,不用写func
(2)构造函数的作用
分配空间 alloc
设置初始值 init
3、必选属性
(1)非可选属性就是必选属性
(2)如果一个类中定义了必选属性,必须通过构造函数为这些必选属性分配空间并且设置初始值
4、Optional 可选属性
(1)可选属性不需要设置初始值,默认初始值都是 nil
(2)可选属性是在设置数值的时候才分配空间的,是延迟分配空间的,更加符合移动开发中延迟创建的原则
(3)注意,普通数据类型如Int,在设置为可选,并且没有初始化时,也是nil,在与OC的交互中就会出问题,因为OC的普通类型都有初始值,比如Int = 0 ,所以要根据情况,给可选属性初始化。。。 这种情况出问题的,比如KVC给模型属性赋值。
5、override init() 方法的特殊性 (其中有些争议)
(1)先给自己的必选属性赋值
(2)再调用super.init()初始化父类的属性
(3)其他重载的构造函数不是这个顺序,要先调用super实例化对象,再给自己的必选属性赋值
(4)关于系统提供的默认构造函数:Xcode 7 beta 5之后,父类的构造函数会被自动调用(也就是说,可以不用写super.init()),强烈建议写 super.init(),保持代码执行线索的可读性(这里应该是只有父类系统会自动调用。。)
(5)重载的构造函数,系统不会调用默认构造函数 ( 这句话有待商榷,实验中,也是调了,个人感觉,只要是继承自NSObject的父类,系统都会帮我们调默认的构造函数,而子类就不会,因为默认的构造函数满足不了需求)
十四、重载与重写
1、重载定义:
(1)面向对象的一个重要特征,oc里面没有
(2)函数名相同,参数名或者参数个数不同就是重载,不仅限于构造函数
(3) 函数重载能够简化程序员的记忆
2、重写定义:
(1)必须有继承关系的函数,对父类函数功能的重写和完善
(2)override关键字
3、注意事项
(1)如果重载了构造函数,但是没有实现默认的构造函数 init(),则系统不再提供默认的构造函数。原因,在实例化对象时,必须通过构造函数为对象属性分配空间和设置初始值,对于存在必选参数的类而言,默认的 init() 无法完成分配空间和设置初始值的工作(这里也是有待商榷,实验证明,父类不管是否实现 init(),重载了构造函数,系统依然会调用默认的构造函数)
(2)在子类中,如果是重载的构造函数,必须 super 以完成父类属性的初始化工作
(3)重写时,子类需要在父类拥有方法的基础上进行扩展,需要 override 关键字,父类没有方法,重写不了!
(4)重写的是构造方法时,也要注意super一下,除非你是父类,系统帮你super.init(这是实验结果)
十五、KVC构造函数
1、KVC本质
(1)KVC 是 OC 特有的,KVC 本质上是在运行时,动态向对象发送 setValue:ForKey: 方法,为对象的属性设置数值
(2)在使用 KVC 方法之前,需要确保对象已经被正确实例化
2、关于基本数据类型
(1)在 Swift 中,如果属性是可选的,在初始化时,不会为该属性分配空间
(2)OC 中基本数据类型就是保存一个数值,不存在可选的概念,比如Int 默认是0,不是nil
(3)使用KVC进行属性赋值时,如果基本数据类型的属性没有初始化,就会报错,所以,需要初始化一个数值
报错举例:this class is not key value coding-compliant for the key age. ->这个类的键值 age 与 键值编码不兼容
3、KVC 函数调用顺序
(1) setValuesForKeysWithDictionary 会按照字典中的 key 重复调用 setValue(value, forKey) 函数
(2) 如果没有实现 forUndefinedKey 函数,程序会直接崩溃,NSObject 默认在发现没有定义的键值时,会抛出 NSUndefinedKeyException 异常
如果实现了 forUndefinedKey,会保证 setValuesForKeysWithDictionary 继续遍历后续的 key
(3) 如果父类实现了 forUndefinedKey,子类可以不必再实现此函数
(4)补充:setValue(value, forKeyPath) 这个方法默认也是调用 setValue(value, forKey),不要在后者中去实现前者,否则死循环
(5)如果实现setValue(value, forKey)方法,不要忘记super
4、补充:通过KVC给只读属性赋值,例:自定义tabBar
//自定义tabBar
let tab = ZQTabBarView()
//使用kvc,给readonly属性赋值
setValue(tab, forKey: "tabBar")
十六、convenience 便利构造函数
1、定义
(1)可以返回nil的构造函数(可失败的构造函数)
(2)有关键字convenience
(3)不能被重写
(4)不能使用super
(5)使用self 来调用自己的其他指定构造函数,比如:self.init()
(6)init后面有?
2、指定构造函数
(1)默认情况下,所有的构造方法都是指定构造函数 Designated
(2)除了便利构造函数
3、示例 (在 Xcode 中,输入 self.init 时没有智能提示)
convenience init?(name: String, age: Int) {
if age < 20 || age > 100 {
return nil
}
self.init(dict: ["name": name, "age": age])
}
4、便利构造函数应用场景
(1)根据给定参数判断是否创建对象,而不像指定构造函数那样必须要实例化一个对象出来
(2)在实际开发中,可以对已有类的构造函数进行扩展,利用便利构造函数,简化对象的创建
5、再次注意
(1)指定构造函数必须调用其直接父类的的指定构造函数(除非没有父类,系统调),也就是必须调一下指定构造函数
(2)便利构造函数可以返回 nil
(3)便利构造函数不能被重写
(4)便利构造函数必须调用同一类中定义的其他指定构造函数,如果自己没有指定构造函数,则用 self. 的方式调用父类的便利构造函数,再或者通过self.的方式调用父类的指定构造函数(子类从父类继承)
6、图示:(子类便利构造函数调用父类便利构造函数的情况没有描述)
十七、懒加载
1、意义如同OC中的懒加载,swift懒加载本质上是一个闭包,在使用的时候才会创建
2、懒加载的格式:关键字 lazy
(1)闭包写法:定义一个闭包,并执行了,这里语句 “ ()-> Person in” 省略了,只有懒加载可以
lazy var person: Person = {
return Person()
}()
(2)同上
let personFunc = { () -> Person in
return Person()
}
lazy var demoPerson: Person = self.personFunc()
(3)懒加载的简单写法 :定义个变量,前面加上lazy
lazy var demoPerson: Person = Person()
3、注意:与 OC 不同的是,lazy 属性即使被设置为 nil 也不会被再次调用
十八、只读属性和重写set方法
1、swift的 set和get方法
(1)在 Swift 中 getter & setter 很少用
(2)示例:
private var _name: String?
var name: String? {
get {
return _name
}
set {
_name = newValue
}
}
(3)经常使用的重写set方法
var person: Person? {
didSet {
nameLabel.text = person?.name
ageLabel.text = "\(person?.age ?? 0)"
}
}
2、存储型属性
(1)需要开辟空间,以存储数据
3、计算型属性(只读)
(1)执行函数返回其他内存地址
(2)只实现 getter 方法的属性被称为计算型属性,等同于 OC 中的 ReadOnly 属性
var title: String {
get {
return "Mr " + (name ?? "")
}
}
简写
var title: String {
return "Mr " + (name ?? "")
}
(3)计算型属性本身不占用内存空间
(4)不可以给计算型属性设置数值
(5)只读型更像一个函数,不过不能接收参数,同时必须有返回值
(6)每次调用时都会被执行,这里和懒加载不同
十八、网络访问(不使用AFN)
1、同OC ,在iOS7.0以后,默认使用https,要设置白名单(ATS)强制访问
<key>NSAppTransportSecurity</key>
<dict>
<!--Include to allow all connections (DANGER)-->
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
2、JSON的反序列化:与OC略不同
(1)try! 向系统保证我的数据一定是一个json
let result = try! NSJSONSerialization.JSONObjectWithData(data, options: [])
(2) try? 如果是json就反序化成功,否则直接返回nil
let result = try? NSJSONSerialization.JSONObjectWithData(data, options: [])
(3) try catch 判断序列化的时候是否出现异常,如果有异常则进入catch,否则反序列化成功
do {
let result = try NSJSONSerialization.JSONObjectWithData(d, options: [])
print(result)
} catch {
print(error)
}
十九、swift使用pod
1、如同OC正常安装pod
2、在podfile文件中贴入(可以使用vim编辑器),第一行不可少,使用OC框架可以不用桥接(不知道什么鬼。。)
use_frameworks!
platform :ios, '8.0'
pod 'SDWebImage'
pod 'SVProgressHUD'
pod 'SnapKit'
pod 'FMDB'
pod 'AFNetworking'
二十、swift和OC 的桥接
1、正常情况下,swift想要使用oc的文件需要桥接
2、步骤:
(1)创建一个 header.h 文件(要求和 info.plist同级)
(2)找到bridge的配置路径,把 header.h路径放入配置文件里:
配置文件步骤 buildSettings - > 查找bridge - > 设置路径(和info.plist同级)
(3)到 header.h 文件里 ,导入 OC框架的头文件,需要哪个导入哪个
二十一、extension 和 description
1、extension 类似于 OC 的 Category,可以按照函数类型区分代码,能够让代码结构具有更好的可读性
2、重写 description 属性(只读属性),便于调试。 在类中定义如下,将成员属性作为key传入,然后打印这个类的对象就可以显示出准确内容
例:
override var description: String {
let keys = ["name", "age"]
return dictionaryWithValuesForKeys(keys).description
}
二十二、自定义DEBUG
1、如同OC中的PCH文件,设置自定义的DEBUG,可以随时调整关闭DEBUG
2、步骤
(1)创建swift file 文件
(2)贴入下面内容
/// 输出日志
/// - parameter message: 日志消息
/// - parameter logError: 错误标记,默认是 false,如果是 true,发布时仍然会输出
/// - parameter file: 文件名
/// - parameter method: 方法名
/// - parameter line: 代码行数
func printLog<T>(message: T,
logError: Bool = false,
file: String = __FILE__,
method: String = __FUNCTION__,
line: Int = __LINE__)
{
if logError {
print("\((file as NSString).lastPathComponent)[\(line)], \(method): \(message)")
} else {
#if DEBUG
print("\((file as NSString).lastPathComponent)[\(line)], \(method): \(message)")
#endif
}
}
(3)配置:buildingSetting -> 查找 swift flags - >在下方设置debug标记 - > 点击加号 -> 添加 : -D DEBUG
(4)程序发布前,删掉 DEBUG即可
(5)另外,在此文件中定义的变量、常量,可以全局访问,类似PCH文件
二十三、SpanKit 和 SVProgressHUD 的使用
1、SpanKit用来进行页面布局的三方控件(约束),等同于OC中的 masnory
示例
registerButton.snp_makeConstraints { (make) -> Void in
make.left.equalTo(noticeLabel.snp_left)
make.top.equalTo(noticeLabel.snp_bottom).offset(10)
make.width.equalTo(100)
make.height.equalTo(30)
}
2、SVProgressHUD 简单使用
示例
func webViewDidStartLoad(webView: UIWebView) {
SVProgressHUD.show()
}
func webViewDidFinishLoad(webView: UIWebView) {
SVProgressHUD.dismiss()
}
/// 关闭
func close() {
SVProgressHUD.dismiss()
dismissViewControllerAnimated(true, completion: nil)
}