《千万别说swift不用管理内存!这么多坑赶快记下》
聊一聊,Swift的内存管理 在Objective-C中无论ARC、MRC,开发中我们还是需要对内存进行管理,主要在于,避免循环引用、代理、KVO移除、Block这些,再有就是属性声明时的语义(property_attribute)。关于Objective-C中的引用计数制我就不重复了。 可到了Swift中,对于属性的声明就只剩下了lazyweak。这个可让这些从Objective-C走来的程序员们一下就犯了难。 其实并非如此,Swift中依然需要所有权声明,请大家一定要认真阅读下边的内容。
当你每次创建一个类的新的实例的时候,ARC 会分配一大块内存用来储存实例的信息。内存中会包含实例的类型信息,以及这个实例所有相关属性的值。 此外,当实例不再被使用时,ARC 释放实例所占用的内存,并让释放的内存能挪作他用。这确保了不再被使用的实例,不会一直占用内存空间。 然而,当 ARC 收回和释放了正在被使用中的实例,该实例的属性和方法将不能再被访问和调用。实际上,如果你试图访问这个实例,你的应用程序很可能会崩溃。 为了确保使用中的实例不会被销毁,ARC 会跟踪和计算每一个实例正在被多少属性,常量和变量所引用。哪怕实例的引用数为1,ARC都不会销毁这个实例。 为了使上述成为可能,无论你将实例赋值给属性、常量或变量,它们都会创建此实例的强引用。之所以称之为“强”引用,是因为它会将实例牢牢地保持住,只要强引用还在,实例是不允许被销毁的。
接下来的代码片段定义了三个类型为Person?的变量,用来按照代码片段中的顺序,为新的Person实例建立多个引用。由于这些变量是被定义为可选类型(Person?,而不是Person),它们的值会被自动初始化为nil,目前还不会引用到Person类的实例。 var reference1: Person? var reference3: Person?
reference1 = Person(name: "John Appleseed") // prints "John Appleseed is being initialized” 应当注意到当你调用Person类的构造函数的时候,“John Appleseed is being initialized”会被打印出来。由此可以确定构造函数被执行。 由于Person类的新实例被赋值给了reference1变量,所以reference1到Person类的新实例之间建立了一个强引用。正是因为这一个强引用,ARC 会保证Person实例被保持在内存中不被销毁。 如果你将同一个Person实例也赋值给其他两个变量,该实例又会多出两个强引用: reference2 = reference1 reference3 = reference1 现在这一个Person实例已经有三个强引用了。 如果你通过给其中两个变量赋值nil的方式断开两个强引用(包括最先的那个强引用),只留下一个强引用,Person实例不会被销毁: reference1 = nil reference2 = nil 在你清楚地表明不再使用这个Person实例时,即第三个也就是最后一个强引用被断开时,ARC 会销毁它: reference3 = nil // 打印 “John Appleseed is being deinitialized” 3、类实例之间的循环强引用 我们可能会写出一个类实例的强引用数永远不能变成0的代码。如果两个类实例互相持有对方的强引用,因而每个实例都让对方一直存在,就是这种情况。这就是所谓的循环强引用。 你可以通过定义类之间的关系为weak弱引用或unowned无主引用,以替代强引用,从而解决循环强引用的问题。 class Apartment { }
在 Objective – C 框架链接的应用程序,通常在它们的每一个线程必须创建至少 一个自动释放池。如果应用程序使用管理模型,即应用程序处理的 retain 和 release 对象,那么自动释放池捕获任何从该线程 autorelease 的对象。在Swift框架下呢??最近在Spritekit下做了一个Swift的demo。然后为了加载时高效一点,在子线程中加载背景,结果发现内存永远的增涨,以0.2MB,0.2MB的固定速度增涨,
排查最后,发现刚好背景图片切片的大小是0.2MB。这样的话,想来是没有别的原因了。我已经按照,正常的逻辑,在画面离开屏幕时就删除节点了,但是内存仍然在增加。想来也是没有其他原因。 在Swift中,子线程的内存仍然需要添加自动释放池,结果Swift开发指南中,并没有提到。 不过还是在Swifter中找到了,我想要的。 在 Swift 中我们也是能使用 autoreleasepool 的 — 虽然语法上略有不同。相比于原来在 Objective-C 中的关键字,现在它变成了一个接受闭包的方法: func autoreleasepool(code: () -> ()) 利用尾随闭包的写法,很容易就能在 Swift 中加入一个类似的自动释放池了: func loadBigData() { } 这样改动以后。果然程序变得舒服多了。 经过ARC和Swift的双重隐藏,内存管理已经越来越不显眼了,但是其中的坑依然不少,希望大家通过本文,能加深对Swift的理解。 长按下方二维码关注最新推送的技术博客! (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |