Swift中的内存管理
源:http://www.jianshu.com/p/6bef235878c8
文/Sheepy(简书作者) 原文链接:http://www.jianshu.com/p/6bef235878c8 著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”。 之前用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的运作方式,从中不难看出这种策略的缺陷,就是循环引用问题。看下图: 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() } } }
这是我自己封装的一个下拉刷新控制器,它继承自 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秒,同时执行 但是这里出现了循环引用问题,我们来看看它是怎么发生的。在 接下来,在初始化 要如何打破僵局呢,其实也很简单,使用 //refreshCtrl指向的对象只持有当前HouseTableCtrl对象的一个弱引用 let refreshCtrl = SimpleRefreshCtrl { [weak self] in self?.getPageData() } //这一句强引用 self.refreshControl = refreshCtrl
这样 至于 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |