Swift使用自动引用计数(ARC)来管理应用程序的内存使用。在大多是情况下,并不需要考虑内存的管理。当实例不再需要的时候,ARC会自动释放这些实例所使用的内存。
但ARC并不是绝对安全的。下面两种情况会发生内存泄露。
1,类实例之间的循环强引用
两个类实例都有一个强引用指向对方,这样的情况就是强引用循环,从而导致内存泄露。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | class Teacher { var tName : String var student : Student ? init (name: String ){ tName = name println ( "老师\(tName)实例初始化完成" ) } deinit{ println ( "老师\(tName)实例反初始化完成" ) } } class Student { var sName : String var teacher : Teacher ? init (name: String ){ sName = name println ( "学生\(sName)实例初始化完成" ) } deinit{ println ( "学生\(sName)实例反初始化完成" ) } } //测试开始 var teacher: Teacher ? var student: Student ? teacher = Teacher (name: "李老师" ) student = Student (name: "刘同学" ) teacher!.student = student student!.teacher = teacher teacher = nil student = nil //测试结果(deinit未调用,则内存泄露) 老师李老师实例初始化完成 学生刘同学实例初始化完成 |
解决办法:使用弱引用
只需要将上述例子Teacher类的student变量加上关键字weak,或者将Student类的teacher变量加上关键字weak。
当A类中包含有B类的弱引用的实例,同时,B类中存在A的强引用实例时,如果A释放,也不会影响B的释放。但A的内存回收要等到B的实例释放后才可以回收。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | class Teacher { var tName : String weak var student : Student ? init (name: String ){ tName = name println ( "老师\(tName)实例初始化完成" ) } deinit{ println ( "老师\(tName)实例反初始化完成" ) } } class Student { var sName : String var teacher : Teacher ? init (name: String ){ sName = name println ( "学生\(sName)实例初始化完成" ) } deinit{ println ( "学生\(sName)实例反初始化完成" ) } } |
2,闭包引起的循环强引用
将一个闭包赋值给类实例的某个属性,并且这个闭包体中又使用了实例,也会发生强引用循环。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | class JsonElement { let name: String let jValue: String ? lazy var asJson:() -> String = { if let text = self .jValue { return "\(self.name):\(text)" } else { return "text is nil" } } init (name: String , text: String ){ self .name = name self .jValue = text println ( "初始化闭包" ) } deinit{ println ( "闭包释放" ) } } //开始测试 var p: JsonElement ? = JsonElement (name: "p" , text: "hangge.com" ) println (p!.asJson()) p = nil //测试结果(deinit未调用,则内存泄露) 初始化闭包 p:hangge.com |
解决办法:使用闭包捕获列表
当闭包和实例之间总是引用对方并且同时释放时,定义闭包捕获列表为无主引用。但捕获引用可能为nil时,定义捕获列表为弱引用。弱引用通常是可选类型,并且在实例释放后被设置为nil。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | class JsonElement { let name: String let jValue: String ? lazy var asJson:() -> String = { [ unowned self ] in //使用无主引用来解决强引用循环 if let text = self .jValue { return "\(self.name):\(text)" } else { return "text is nil" } } init (name: String , text: String ){ self .name = name self .jValue = text println ( "初始化闭包" ) } deinit{ println ( "闭包释放" ) } } |