博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Swift - 内存泄露原因(循环强引用)及解决办法
阅读量:5023 次
发布时间:2019-06-12

本文共 2618 字,大约阅读时间需要 8 分钟。

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
(
"闭包释放"
)
    
}
}

转载于:https://www.cnblogs.com/Free-Thinker/p/4841131.html

你可能感兴趣的文章
asp.net mvc 错误处理 - 自定义报错处理,生成错误日志
查看>>
Linux centos ssh
查看>>
R语言之避免for循环示例
查看>>
[转]jQuery 选择器和dom操作
查看>>
Jenkins+Maven+SVN快速搭建持续集成环境(转)
查看>>
bootstrap 媒体查询
查看>>
杜教筛
查看>>
《Ext JS模板与组件基本知识框架图----模板》
查看>>
txmpp
查看>>
微信开发时调用jssdk,在安卓设备中成功调用;在ios设备中返回错误消息:config fail,无其他具体错误消息,且接口权限显示获取ok,无法调用...
查看>>
【Github教程】史上最全github使用方法:github入门到精通
查看>>
抽象工厂模式(Abstract Factory)
查看>>
luogu1373 小a和uim之大逃离 (dp)
查看>>
Redis的Pub/Sub客户端实现
查看>>
SQL日常问题和技巧——持续更新
查看>>
springMVC入门(一)------springMVC基本概念与安装
查看>>
Sam做题记录
查看>>
[bzoj] 2453 维护数列 || 单点修改分块
查看>>
IIS版本变迁
查看>>
BZOJ3884: 上帝与集合的正确用法 拓展欧拉定理
查看>>