前言:
1.此文中的语法会根据Swift的升级变动而更新。
2.如果需要请移步 -> swift2.2 语法(上)、swift 2.2语法(中)
类的析构函数
-
swift会自动释放不需要的实例来达到释放资源的目的
swift通过自动引用计数(ARC)管理内存
当引用计数为0的时候,系统会自动调用析构函数(析构函数无法手动调用)
通常在析构函数内释放一些资源(移除通知、释放不需要对象等)
-
格式:
deinit { 需要执行的操作 }
这个比较好理解,就直接上代码了
class person: NSObject {
// KVC方式下,对象、结构体类型必须是可选类型,否则无法转换
var name : String?
// 自定义构造函数,覆盖init:函数
init(dict : [String : NSObject]) {
// 必须先初始化对象
super.init()
// 调用对象的KVC方法
setValuesForKeysWithDictionary(dict)
}
deinit {
print("移除通知")
print("释放各种资源")
}
}
// 创建person对象
var ps : person? = person(dict:["name" : "laoWang"])
ps = nil // 当赋值为nil时自动引用计数为0,系统将会调用deinit对资源进行释放
自动引用计数
-
swift也是采用自动引用计数来管理内存
- 当有一个强引用指向某个对象时,这个对象的引用计数会+1
- 如果强引用消失,引用计数会-1
- 但引用计数为0的时候,这个对象就会被系统销毁
-
循环引用
- 一般情况下,ARC会自动帮我们管理内存,但是开发中经常会会出现循环引用的问题,这样就造成资源始终无法释放的现象
- 循环引用如图:
class Person { var dog : Dog? deinit { print("释放person") } } class Dog { var master : Person? deinit { print("释放Dog") } } // 创建对象 var ps : Person? = Person() var d : Dog? = Dog() // 循环引用 ps?.dog = d d?.master = ps // 释放资源失败原因:因为person内有个属性强引用着Dog,而Dog内也有个强引用引用着Person,造成了循环引用,所以即使将ps和d赋值为nil也无法释放对象 ps = nil d = nil
- 解决办法
- swift提供了2中方法来解决循环引用的问题
- 使用weak:这个关键字和OC的__weak一样都是弱引用,当指向的对象呗销毁的时候,会自动将指针指向nil
- 使用unowned:和OC中的__unsafe_unretained相似,当对象销毁时,指针依然指向原来的位置(比较危险,容易引起野指针错误)
<br><br>
```
class Person {
// 声明dog为弱引用
weak var dog : Dog?
deinit {
print("释放person")
}
}
class Dog {
var master : Person?
deinit {
print("释放Dog")
}
}
// 创建对象
var ps : Person? = Person()
var d : Dog? = Dog()
// 循环引用
ps?.dog = d
d?.master = ps
// person和dog对象被释放
ps = nil
d = nil
```
可选链
- 调用的目标可能为nil,如果可选的目标有值,就会调用成功;如果可选目标为nil,调用会返回nil
- 多次调用链接在一起就形成一个调用链,任何一个节点为nil,整个链就会失效
- 可选链使用
-
在可选类型后面+一个?号,可以定义一个可选链;有点像在可选值后放个!号来强制解包得到值
- 当可选值为空的时候,可选链就会失效
- 一般的强制解析会引起运行错误
-
因为可选链的结果可能为nil,所以它返回的值是个可选类型
- 可以通过判断返回的参数是否有值来确定是否成功调用(返回nil说明失败)
class Person1 { var name : String var dog : Dog? init(name : String) { self.name = name } } class Dog { var ball : Ball? func yesOrNo() { print("YES,可选类型有值") // 调用成功 } } class Ball { var price : Double = 0.0 } // 创建对象并设置相互的关联性 let ps = Person1(name: "LiSi") let d = Dog() let ball = Ball() ball.price = 300.0 // 李四拥有一条狗 ps.dog = d // 狗有个球 d.ball = ball // 获取狗的球的价格 let price = ps.dog?.ball?.price print(price) // 结果:300.0 // 给狗一个新的球 ps.dog?.ball = Ball() // 如果返回值有值就会继续执行下面这句,如果nil就不会执行 ps.dog?.yesOrNo()
- 可以通过判断返回的参数是否有值来确定是否成功调用(返回nil说明失败)
-
协议
- 格式:
protocol 协议名 {
协议方法
}
- 遵守协议格式
class 类名 : 类的父类, 协议名,协议名 {
}
// 比如
class ViewController: UIViewController, UITableViewDelegate {
}
- 协议使用
// 定义
protocol testProtocol {
// 协议方法
func test1()
func test2()
}
// 使用(swift中默认情况下所有的协议方法都必须实现,不然会报错)
class Person : testProtocol {
var name : String?
// 实现协议方法
func test1() {
print("test1")
}
func test2() {
print("test2")
}
}
- 协议继承
// 定义
protocol testProtocol {
// 协议方法
func test1()
func test2()
}
protocol test2Protocol : testProtocol {
// 协议方法
func test3()
}
- 代理设计模式(根据协议可以继承的特性,我们将其用于代理设计模式)
// 定义
protocol testProtocol {
// 协议方法
func test1()
}
// 使用(swift中默认情况下所有的协议方法都必须实现,不然会报错)
class Person {
// 定义delegate属性
var delegate : testProtocol
// 自定义构造函数
init(delegate : testProtocol) {
self.delegate = delegate
}
// 方法
func personTest() {
delegate.test1()
}
}
class Dog : testProtocol {
func test1() {
print("调用test1") // 结果:调用test1
}
}
// 设置代理
let p = Person(delegate: Dog())
// 实现代理方法
p.personTest()
- 协议方法不需要全部实现的解决方法
// 如果不是每个协议方法都必须实现只需这样写
@objc
protocol testProtocol {
// 协议方法
optional func test1()
}
闭包
-
闭包类似于OC中的Block
- swift的闭包是特殊的函数,OC的Block是匿名函数
- 和block一样,闭包也经常用于回调
闭包的格式
(形参列表)->(返回值)
- 来个简易网络请求举例吧,用block做对比
// block形式
- (void)downloadData:(void (^)())block
{
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"下载数据:%@", [NSThread currentThread]);
dispatch_sync(dispatch_get_main_queue(), ^{
// 调用block
block();
});
});
}
- (void)test
{
[self downloadData:^{
NSLog(@"block块: %@", [NSThread currentThread]);
}];
}
// 闭包形式
func downloadData(block : () -> ()) {
dispatch_async(dispatch_get_global_queue(0, 0)) { () -> Void in
print("下载数据:\(NSThread.currentThread())")
dispatch_sync(dispatch_get_main_queue(), { () -> Void in
block()
})
}
}
func test() {
downloadData { () -> () in
print("block块:\(NSThread.currentThread())")
}
}
-
闭包简写
- 当闭包没有参数、返回值的时候, in和.in之前的内容可以省略不写
downloadData ({ print("block块:\(NSThread.currentThread())") })
-
尾随闭包
- 如果闭包是函数的最后一个参数,可以将闭包写在()后面
downloadData(){ () -> () in print("block块:\(NSThread.currentThread())") }
- 函数只有一个参数,并且这个参数是闭包,那()可以忽略
downloadData { () -> () in print("block块:\(NSThread.currentThread())") }
- 如果闭包是函数的最后一个参数,可以将闭包写在()后面
- 当闭包没有参数、返回值的时候, in和.in之前的内容可以省略不写
闭包的循环引用
- 如果在函数中对闭包进行了强引用,会造成循环引用
- 这边我们先来搞个循环引用的例子
class download : NSObject {
// 定义变量来强引用block
var block : (()->())?
func downloadData(block : ()->()) {
dispatch_async(dispatch_get_global_queue(0, 0)) { () -> Void in
print("下载数据:\(NSThread.currentThread())")
dispatch_sync(dispatch_get_main_queue(), { () -> Void in
block()
})
// 制造循环引用
self.block = block
}
}
deinit {
print("deinit")
}
}
var d : download? = download()
d?.downloadData { () -> () in
print("调")
}
d = nil
-
循环引用解决方法
- 方法一:最先想到的肯定是使用weak修饰当前控制器,但是self可能有值也可能为nil,所以weakself是可选类型,在使用的时候需要对它进行强制解包操作(这边可以直接解包,因为如果控制器不存在,就不可能调用函数)
// 且必须是变量,不能是常量 weak var weakSelf = self
- 方法二:使用unowned关键字,缺点就是容易造成野指针的现象,因为即使对象被释放,它的指针还是指向原来的位置,不会自动指向nil
unowned var unSelf = self
- 方法一:最先想到的肯定是使用weak修饰当前控制器,但是self可能有值也可能为nil,所以weakself是可选类型,在使用的时候需要对它进行强制解包操作(这边可以直接解包,因为如果控制器不存在,就不可能调用函数)
懒加载
- 懒加载就是在我们需要使用的时候才会真正加载到内存中的一种编程思想
- swift中使用 'lazy' 关键字来实现懒加载
- 懒加载本质就是在第一次使用的时候执行闭包,把闭包的返回值赋值给属性,而且只会赋值一次
- 格式:
lazy var 变量名 : 类型 = {
创建代码
}()
- 懒加载使用
lazy var test : [Int] = {
() -> [Int] in
return 27
}()
异常处理(抛异常提示)
- 开发中,经常会出现错误,swift设计的时候就已经考虑到这方面,尽可能多地帮我们明确错误原因
- 在swift中,任何一个遵守ErrorType protocol的类型,都可以用来描述错误
- ErrorType是个空protocol(协议),它功能很单一,就是用来告诉编译器,某个类型用来表示错误
- 我们常会定义一个枚举(enum)来确定各种可能出现的错误
- 来简单模拟下异常情况
// 假定这个方法我们不知道他的实现方式,如果不错任何处理,那么就很难猜中需要传的值
func test(name : String) -> String? {
guard name == "laoWang" else {
return nil
}
guard name == "laoLi" else {
return nil
}
return "true"
}
test("12") // 结果为nil
- 使用抛异常的方式来尽可能告诉使用者错误原因
// 定义异常情况枚举
enum error : ErrorType {
case errorNameNotForLaoWangOrLaoLi
}
// 假定这个方法我们不知道他的实现方式,如果不错任何处理,那么就很难猜中需要传的值
func test(name : String) throws -> String {
guard name == "LaoWang" else {
throw error.errorNameNotForLaoWangOrLaoLi
}
guard name == "LaoLi" else {
throw error.errorNameNotForLaoWangOrLaoLi
}
return "true"
}
// 处理方法一 try!:告诉系统调用没有异常,如果出现异常,程序会崩溃
try! test("lao") // 结果 fatal error: 'try!' expression unexpectedly raised an error: error.errorNameNotForLaoWangOrLaoLi: file /Library/Caches/com.apple.xbs/Sources/swiftlang/swiftlang-700.1.101.15/src/swift/stdlib/public/core/ErrorType.swift, line 50
// 处理方法二 try? 出现异常返回nil,没有异常返回对应的值(返回的结果是个可选类型)
let result = try? test("hh") // 结果 nil
// 处理方法三 try 需要手动处理异常
do {
let result = try test("hh")
} catch {
print(error) // 结果 errorNameNotForLaoWangOrLaoLi
}
补充
-
注释
- 分组注释 -> // MARK:-
- swift中不可以再使用 #pragma mark -
- 文档注释 -> ///
- 单行注释 -> //
- 多行注释 -> /* */
- 分组注释 -> // MARK:-
-
访问权限
- swift中访问控制基于源文件,不是基于类
- internal -> 只要在本模块中可以访问(默认,所以可以不写)
- private -> 在当前文件中可以访问
- public -> 在其他模块中可以访问(也就是只要在项目中就可以使用它)
- swift中访问控制基于源文件,不是基于类