余晖落尽暮晚霞,黄昏迟暮远山寻
本站
当前位置:网站首页 > 编程知识 > 正文

Swift中的内存管理(swift存储属性)

xiyangw 2022-12-03 13:18 14 浏览 0 评论

之前用Swift写了一个App,已经在App Store上架了。前两天更新了一些功能,然后用Instruments检查的时候,发现有内存泄漏问题。有些同学可能觉得奇怪,Swift不是使用ARC自动管理内存的么,怎么也会发生内存泄漏呢。是会的,但几乎都是由于操作不当造成循环引用(strong reference cycle/retain cycle)导致的。

ARC与GC

很多人分不清ARC(Automatic Reference Counting,自动引用计数)跟GC(Garbage Collection,垃圾收集)的区别。其实“引用计数法”也算是一种GC策略,只不过我们现在提到GC的时候一般是指基于“标记-整理”策略的垃圾收集器,譬如主流的JVM(Java虚拟机)几乎都是采用“标记-整理”+“分代收集”的策略来进行自动内存管理的。标记算法一般是从全局对象图的“根”出发进行可达性分析,对象的生死会被批量地标记出来,之后再在某个时间批量地释放死对象。显然,这是一种“全局+延时”的管理策略。

而与之相对的,引用计数是一种“局部+即时”的内存管理策略。它不需要全局的对象信息,一般每个被管理的对象都会跟一个引用计数器关联,这个计数器保存着当前对象被引用的次数,一旦创建一个新的引用指向该对象,引用计数就加1,每当指向该对象的某个引用失效引用计数就减1,直到引用计数为0,就立即释放该对象。使用引用计数法管理内存的语言也不止OC和Swift,还有诸如CPython之类的GC也是基于引用计数的。

早年OC是采用MRC(手动引用计数)的,当然其实现在也有人还在用,它跟ARC的主要区别在于它需要手动管理引用计数器,而ARC是自动管理的。所以其实MRC也不能让你直接释放对象的,只是控制引用罢了。

循环引用

上面解释了一下ARC的运作方式,从中不难看出这种策略的缺陷,就是循环引用问题。看下图:

reference_cycle.png

object1和object1之间形成了循环引用,它们的引用计数始终为1,始终不会被释放,这就造成了内存泄漏。“标记-整理”策略并不会出现这种问题,因为哪怕两个对象相互引用,但只要它们和“根”对象失去了联系,照样会被标记为死对象,然后在合适的时间被释放。

实例分析

接下来看一个稍微复杂一点的实例,分析一下出现循环引用的原因然后给出解决方法。

class SimpleRefreshCtrl: UIRefreshControl { typealias Action = () -> () var action: Action! init(action: Action) { super.init()

tintColor = UIColor.navigationBarColor() self.action = action self.addTarget(self, action: "refresh", forControlEvents: UIControlEvents.ValueChanged)

}

required init?(coder aDecoder: NSCoder) {

fatalError("init(coder:) has not been implemented")

} func refresh() { self.action()

delay(seconds: 1) { self.endRefreshing()

}

}

}

这是我自己封装的一个下拉刷新控制器,它继承自UIRefreshControl,可以在UITableViewController中直接使用,如下:

class HouseTableCtrl: UITableViewController { //...

func getPageData() {

getListFromApi(urlString) { json, nextLink in

self.houseData = json self.page = nextLink

}

} override func viewDidLoad() { super.viewDidLoad() let refreshCtrl = SimpleRefreshCtrl(action: getPageData) self.refreshControl = refreshCtrl //...

}

}

这样,当你下拉列表的时候,旋转的菊花就会出现旋转1秒,同时执行getPageData方法,刷新页面数据。

但是这里出现了循环引用问题,我们来看看它是怎么发生的。在getPageData方法中我调用了一个全局函数getListFromApi,而这个全局函数需要一个闭包作为参数,而这个闭包又捕获了当前对象的两个属性,也就持有了当前对象的引用。到这里为止并没有什么问题,虽然闭包捕获外部变量从而持有外部对象的引用经常是造成循环引用的一大元凶,但在这里,该闭包是个匿名闭包,我们的HouseTableCtrl对象并没有持有该闭包的引用,所以问题并不是出在这里。

接下来,在初始化SimpleRefreshCtrl对象的时候,getPageData作为参数被传递了过去,并被赋值给SimpleRefreshCtrl的实例属性action。注意,getPageData是在HouseTableCtrl中定义的一个实例方法,是跟当前的HouseTableCtrl对象关联的,作为参数传递过去的实际上是self.getPageData。如此一来,SimpleRefreshCtrl对象就持有了当前HouseTableCtrl对象的引用。然后接下来这一句self.refreshControl = refreshCtrl,持有HouseTableCtrl对象引用的SimpleRefreshCtrl对象被赋值给了HouseTableCtrl的实例属性refreshControl,于是HouseTableCtrl对象也持有了SimpleRefreshCtrl对象的引用。这就造成了循环引用。

要如何打破僵局呢,其实也很简单,使用weak或者unowned就行了:

//refreshCtrl指向的对象只持有当前HouseTableCtrl对象的一个弱引用let refreshCtrl = SimpleRefreshCtrl { [weak self] in

self?.getPageData()

}//这一句强引用self.refreshControl = refreshCtrl

这样SimpleRefreshCtrl对象就只是持有当前HouseTableCtrl对象的一个弱引用,弱引用是不算在HouseTableCtrl对象的引用计数中的,也就是说当没有其他引用指向HouseTableCtrl对象时,HouseTableCtrl对象能被正常释放,一旦HouseTableCtrl对象被释放了,那SimpleRefreshCtrl对象也就能被正常释放了:

weak_reference.png

至于weakunowned该用哪个么,看情况了,weak修饰的属性或变量是一个optional类型,也就是说是可以为nil的。而unowned则是修饰一个nonoptional,是不能为nil的,一旦这个属性或变量指向的对象被释放了(这是有可能发生的,因为unowned引用也是不算在引用计数中的,如果除了unowned引用外没有其他引用指向那个对象,那它将被释放),而你还想使用该对象的话,将会触发runtime error,程序也就crash了。所以个人来说,我是更推荐使用weak的。

[本文转自简书,作者Sheepy ,转载请注明来自威客安全]

相关推荐

Web前端培训:为什么JavaScript对Web开发人员来说是必不可少的
Web前端培训:为什么JavaScript对Web开发人员来说是必不可少的

JavaScript是一种编程语言,使你能够在网页上实现复杂的功能,如交互式地图、及时的内容更新、动画2D或3D图形等等!它是当前标准web技术的第三层,另外两...

2023-03-21 19:18 xiyangw

为什么JavaScript活得这么好?总能踩到未来的步点!
为什么JavaScript活得这么好?总能踩到未来的步点!

为什么JavaScript活得很好,以及它未来在区块链进入主流的过程中将扮演怎样的角色?JavaScript很活跃。JavaScript不仅是全球最知名的编程语...

2023-03-21 19:18 xiyangw

前端开发基础,JavaScript 主要作用是什么?(javascript前端开发案例教程)

学习编程的同学对JavaScript并不陌生,JavaScript是前端技术中非常重要的内容。例如页面的效果切换、动画效果、页面游戏等等。其实我们功通过HTML和CSS技术已经可以搭建各种样式的页面,...

需要了解的5种现代JavaScript特性(应用需要浏览器开启 javascript 支持!)
需要了解的5种现代JavaScript特性(应用需要浏览器开启 javascript 支持!)

JavaScript是最流行的脚本或编程语言之一,在开发专业网页时包含许多功能。它是Web技术的第三个最重要的层,仅次于HTML和CSS。Jav...

2023-03-21 19:17 xiyangw

为什么每个 Web 新手开发人员都需要学习JavaScript
为什么每个 Web 新手开发人员都需要学习JavaScript

当你刚刚进入Web开发领域时,JavaScript是必须要学的一门语言之一,web开发是绝对少不了JavaScript的。为什么初学者必须要学这门语言呢?...

2023-03-21 19:17 xiyangw

说说你所不知道的JavaScript知识(站在十字路口不知道如何选择的说说)

JavaScript(简称js)广泛应用在web开发领域,几乎是web开发的唯一编程语言,近些年,借助node.js的快速发展,js在服务器领域也有了非常广泛运用与拓展。然而,虽然js在前后端都有了一...

JavaScript 真实性能(javascript+jquery)
JavaScript 真实性能(javascript+jquery)

V8JavaScript引擎是Chrome中实现快速浏览的关键因素。过去的一年来,V8团队研究出一种新方法,可根据真实网页快照度量性能。在过去的一年中...

2023-03-21 19:16 xiyangw

现代JavaScript的高级概念和用法(javascript高级语言程序设计)
现代JavaScript的高级概念和用法(javascript高级语言程序设计)

JavaScript语言不断发布一些新特性,感觉要上天的节奏啊。本文搜集整理了一些它的高级概念和用法,来看看你是否都了解?代码这样写是不是更优雅了?闭包闭包是J...

2023-03-21 19:16 xiyangw

用 JavaScript 让时间更人性化(javascript有什么用)

以下是一个JavaScript代码段,它可以将传入的字符串类型日期转换为“刚刚”、“几秒前”、“几分钟前”、“几个小时前”、“昨天”、“前天”或“几天前”等形式:“想象一下,你正在设计一个社交网站...

10 项必须知道的技术提升你的 JavaScript 技能
10 项必须知道的技术提升你的 JavaScript 技能

JavaScript是一种非常强大的编程语言,它通过提供构建动态和交互式网站的方法在Web开发中找到了自己的位置。它在开发人员和Web设计人员中的流行...

2023-03-21 19:15 xiyangw

浅谈JavaScript的用处(javascript以及javascript的作用)
浅谈JavaScript的用处(javascript以及javascript的作用)

我把JS理解为,页bai面的du化妆zhi术。我们dao就是化妆者,通过JS,我们可以把一zhuan个页面全我们想要的shu意思进行渲染,执行相应的功能,生成相...

2023-03-21 19:15 xiyangw

报告显示:JavaScript成为最受欢迎的技术(9 条非常强大的javascript 技巧)
报告显示:JavaScript成为最受欢迎的技术(9 条非常强大的javascript 技巧)

JavaScript成为最受开发人员欢迎的技术。PluralsightTechnologies发布了其技术索引,并发现JavaScript排名第一。去年公司在...

2023-03-21 19:14 xiyangw

JavaScript:5个 JS 解构有趣的用途(解构诡异)

为了保证的可读性,本文采用意译而非直译。1.交换变量通常交换两个变量的方法需要一个额外的临时变量,来看看例子:leta=1;letb=2;lettemp;temp=a;a...

为什么这么多人学习JavaScript?总结是3个原因
为什么这么多人学习JavaScript?总结是3个原因

JavaScript(JS)是一种面向对象的脚本语言,可用于各种不同的项目和创作,从网站开发到机器人编程。尽管JavaScript可能不像某些编程语言(如Pyt...

2023-03-21 19:14 xiyangw

JavaScript的基本精华概念(javascript的含义和作用)

JavaScript的基本精华概念对象JavaScript的简单类型包括数字、字符串、布尔值、null和undefined值。总共5种基本类型其他的所有值都是对象,数字、字符串和布尔值「貌...

取消回复欢迎 发表评论: