1. 析构方法
析构方法
对象的内存被回收前夕被隐式调用的方法,对应OC中的dealloc
方法
主要执行一些额外操作,例如释放一些持有资源,关闭文件,断开网络
class FileHandler {
var fd: Int32? // 文件描述
// 指定构造器
init(path: String){
// 需要打开的文件路径,打开方式(只读)
// open方法是UNIX的方法
let ret = open(path, O_RDONLY)
if ret == -1 {
fd = nil
}else{
fd = ret
}
print("对象被创建")
}
// 析构方法
deinit{
// 关闭文件
if let ofd = fd{
close(ofd)
}
print("对象被销毁")
}
}
var fh: FileHandler? = FileHandler(path: "/Users/gapjun/Desktop/nicai.png")
析构方法的自动继承
父类的析构方法会被自动调用,不需要子类管理
class Person {
var name: String
init (name: String){
self.name = name
print("Person init")
}
deinit{
print("Person deinit")
}
}
class SuperMan: Person {
var age: Int
init(age: Int){
self.age = age
super.init(name: "nicaio")
print("SuperMan init")
}
deinit{
// 如果父类的析构方法不会被自动调用,那么我们还需要关心父类,但是如果这样做子类是比较痛苦的
print("SuperMan deinit")
}
}
var sm: SuperMan? = SuperMan(age: 30)
sm = nil
2. 内存管理
Swift内存管理
管理引用类型的内存,不会管理值类型,值类型不需要管理
内存管理原则: 当没有任何强引用指向对象,系统会自动销毁对象
默认 情况下所有的引用都是强引用
如果做到该原则: ARC
class Person {
var name: String
init(name: String){
self.name = name
}
deinit{
print("deinit")
}
}
var p: Person? = Person(name: "nicia")
p = nil
weak弱引用
class Person1 {
var name: String
init(name: String){
self.name = name
}
deinit{
print("deinit")
}
}
// 强引用,引用计数 +1
var strongP = Person1(name: "TMD") // 1
var strongP2 = strongP // 2
/*
弱引用, 引用计数不变
如果利用weak修饰变量,当对象释放后会自动将变量设置为nil
所以利用weak修饰的变量必定是一个可选类型,因为只有可选类型才能设置为nil
*/
weak var weakP: Person1? = Person1(name: "WOCAO")
if let p = weakP{
print(p)
}else{
print(weakP)
}
unowned: 无主引用,相当于OC unsafe_unretained
unowned与weak的区别
1、利用unowned修饰的变量,对象释放后不会设置为nil,不安全
利用weak修饰的变量,对象释放后会设置为nil
2、利用unowned修饰的变量,不是可选类型
利用weak修饰的变量,是可选类型
class Person2 {
var name: String
init(name: String){
self.name = name
}
deinit{
print("deinit")
}
}
unowned var weakP2: Person2 = Person2(name: "gaojun")
循环引用
ARC不是万能的,它可以的解决内存问题,但是在某些场合不能很好的解决内存泄露问题
例如两个或者多个对象之间的循环引用问题
class Person3 {
let name: String // 姓名
// 人不一定有公寓,如果这里不加weak的话,当对象并没有销毁
weak var apartment: Apartment? // 公寓
init(name: String){
self.name = name
}
deinit{
print("\(self.name) deinit")
}
}
class Apartment {
let number: Int // 房间号
var tenant: Person3? // 租客
init(number: Int){
self.number = number
}
deinit{
print("\(self.number) deinit")
}
}
var p3: Person3? = Person3(name: "wokao")
var a: Apartment? = Apartment(number: 888)
p3!.apartment = a // 人有一套公寓
a!.tenant = p3! // 公寓中住着一个人
p3 = nil
a = nil
class Person4 {
let name: String // 姓名
// 人不一定有信用卡
var card: CreditCard?
init(name: String){
self.name = name
}
deinit{
print("\(self.name) deinit")
}
}
class CreditCard {
let number: Int
// 信用卡必须有所属用户,当某一个变量/常量必须有值,一直有值,那么可以使用unowned修饰
unowned let person: Person4
init(number: Int, person: Person4){
self.number = number
self.person = person
}
deinit{
print("\(self.number) deinit")
}
}
var p4: Person4? = Person4(name: "nima")
var cc: CreditCard? = CreditCard(number: 88888, person: p4!)
p4 = nil
cc = nil
3. 可选类型
可选类型:
可选类型的本质其实就是一个枚举
None没有值
Some有值
格式: Optional<类型> 或者在类型后面加上一个问号
由于可选类型在Swift中随处可见,所以系统做一个语法糖,在类型后面加上?
var opa: Optional
var opb: Int?
var nora: Int
nora = 10
print(nora)
print(opb)
/*
基本类型变量,在使用之前必须进行初始化,否则报错
目的: 安全,不管在什么时候访问都是有意义的
普通变量和可选类型的区别,普通变量都是有意义的
注意: Swift 的变量和C/OC的不一样,C/OC可以没有值,是一个随机值
可选类型是安全的么? 是,可以通过可选绑定判断后再使用
Swift的发明者完全是基于安全的考虑,当我们使用基本类型时完全不用考虑是否有值
当我们使用可选类型时,总会记得先判断再使用,让程序时刻了解哪些有值,哪些没有值
*/
var opc: Int?
opc = 55
if let b = opc{
print(opc!)
print(b)
}
可选链
通过可选类型的变量来调用相应的属性和方法
可选链的返回值是一个可选值
格式:
可选值?.属性
可选值?.方法
class Person {
var name: String
init(name: String){
self.name = name
}
func whoIam() ->String{
print("my name is \(self.name)")
return name
}
}
var p0: Person?
var p1: Person = Person(name: "wocao")
p1.name = "made"
p1.whoIam()
/*
如何通过可选类型来调用对应的方法和属性
1、通过强制解包
但是强制解包非常危险,如果可选类型没有值,会引发运行时错误
p0!.name = "dede"
p0?.whoIam()
2、通过可选绑定,代码繁琐
if let p = p0{
p.name = "made"
p.whoIam()
}
3、通过可选链,如果问号前面的变量没有值,整个可选链会失效
p0 = p1
p0?.name = "dedede"
p0?.whoIam()
可选链的返回值会自动包装成一个可选值
因为可选链可用能失效
所以返回值可能有值,也可能没有值
要想表达有值或者没有值,只能用可选值,所以返回值会自动包装成一个可选值
*/
print(p0?.name)
print(p0?.whoIam())
print(p1.name)
var a: String? = p0?.name
p0?.name = "ww"
var b: String = p1.name
可选链调用下标索引
格式:
可选值?[]
struct Student {
var name:String = "enen"
var math:Double = 99.0
var chinese: Double = 98.0
var english: Double = 97.0
/*
要想实现下标访问,必须实现subscript方法
如果想要通过下标访问,必须实现get方法
如果想要通过下标赋值,必须实现set方法
*/
subscript(course: String) ->Double?{
get{
switch course{
case "math":
return math
case "chinese":
return chinese
case "english":
return english
default:
return nil
}
}
set{
switch course{
case "math":
// 因为返回的是可选类型
math = newValue!
case "chinese":
chinese = newValue!
case "english":
english = newValue!
default:
print("not found")
}
}
}
}
var stu: Student? = Student()
// 可选链调用下标索引不需要,直接在问号后面加上[]即可
print(stu?["math"])
// 利用可选链赋值,注意:早先版本中不能利用可选链赋值
stu?.name = "hehe"
print(stu?.name)
// 利用可选链给下标赋值
stu?["math"] = 88
print(stu?["math"])
/*
判断赋值操作是否成功,可选链的赋值操作也有返回值
如果赋值成功,会返回一个可选类型
返回()?也就是是Void?代表成功
返回nil代表失败
*/
let res: Void? = stu?.name = "haha"
print(res)
stu = nil
let res1: Void? = stu?.name = "haha"
print(res1)
多层可选链
单层:可选值?.属性
多层: 可选值?.属性.属性?.属性 或者 可选值?.属性?.属性?.属性
class A {
var name: String = "heheda"
}
class B {
var a1: A?
}
class C {
var b1: B = B()
}
class D {
var c1: C?
}
var a1 = A()
var b1 = B()
var c1 = C()
var d1 = D()
d1.c1 = c1
// 通过d直接给b赋值,由于D中的C是可选值,所以需要在C后面加上?
d1.c1?.b1.a1 = a1
// 通过d直接获取a中的name,其实只需要在可选值后面加上问号即可,如果可选值不存在,那么后面的链失效
print(d1.c1?.b1.a1?.name)