深入讲解Swift的内存管理

前言

LLVM编译器的好:Swift的内存管理除了要注意引用循环之外,几乎全部被LLVM编译器包揽,不需要开发人员操心。

Swift 是自动管理内存的,这也就是说,我们不再需要操心内存的申请和分配。当我们通过初始化创建一个对象时,Swift 会替我们管理和分配内存。而释放的原则遵循了自动引用计数 (ARC) 的规则:当一个对象没有引用的时候,其内存将会被自动回收。这套机制从很大程度上简化了我们的编码,我们只需要保证在合适的时候将引用置空 (比如超过作用域,或者手动设为 nil 等),就可以确保内存使用不出现问题。

但是,所有的自动引用计数机制都有一个从理论上无法绕过的限制,那就是循环引用 (retain cycle) 的情况。

引用循环问题是什么

Swift 使用 ARC(自动引用计数)的方法为引用类型管理内存。

在 Swift 中,当声明引用类型的变量并将对象负值给他时,相当于创建了对该对象的强引用,该对象的引用计数将加1。如果两个对象相互强引用,将导致引用循环。引用循环一旦出现,相关的对象将无法释放,从而产生内存泄漏。

引用循环问题出现的场景与解决办法

Swift中类对象和闭包都是通过引用进行传值,所以以下场景会出现引用循环:

类对象相互强引用

两个对象彼此引用对方时,形成引用循环。

class Letter {
 let addressedTo: String
 var mailbox : MailBox? 

 init( addressedTo: String) {
 self. addressedTo = addressedTo
 } 

 deinit {
 printl(" The letter addressed to \(addressedTo) is being discarded")
 }
}

class MailBox {
 let poNumber: Int
 var letter : Letter? 

 init( poNumber: Int) {
 self. poNumber = poNumber
 } 

 deinit {
 print(" P.O Box \(poNumber is going away)")
 }
}

Letter 类中强引用了 MailBox 类对象,MailBox 类中又强引用了 Letter 类对象形成引用循环。

解决办法:声明对象时加入 weak 关键字(弱引用)可以解除强引用。比如将 letter 对象声明为 weak 时,mailbox 对象的引用计数不会加1,从而解除引用循环。一般将逻辑上属于另一对象的对象声明为弱对象。如:

weak var letter : Letter?

闭包中引用包含自身的对象

闭包中引用包含自身的对象也会造成引用循环。

class MailChecker {
 let mailbox: MailBox
 let letter: Letter 

 lazy var whoseMail: () -> String = {
 return "Letter is addressed to \(self. letter.addressedTo)"
 }

 init(name: String) {
 self. mailbox = MailBox( poNumber: 311)
 self. letter = Letter( addressedTo: name)
 } 

 deinit {
 println(" class is being deintialized")
 }
}

示例代码中 whoseMail 的闭包中使用 self 引用了包含自身的 MailChecker 对象,此时该闭包拥有 MailChecker 对象,而 MailChecker 对象又拥有该闭包,导致引用循环。

解决办法:此时可以添加[unowned self]让 Swift 知道不应保留 self 对象,从而解除引用循环。将闭包改为:

lazy var whoseMail: () -> String = { [unowned self] in
 return "Letter is addressed to \(self. letter.addressedTo)"
}

注:代码均取自 Boisy G. Pitre《Swift基础教程》

总结

以上就是这篇文章的全部内容了,希望本文的内容对各位iOS开发者们能带来一定的帮助,如果有疑问大家可以留言交流,谢谢大家对我们的支持。

(0)

相关推荐

  • 详谈swift内存管理中的引用计数

    在swift中,每一个对象都有生命周期,当生命周期结束会调用deinit()函数进行释放内存空间. 观察这一段代码: class Person{ var name: String var pet: Pet? init(name: String){ self.name = name print("Person", name, "is initialized") } init(name: String, petName: String){ self.name = nam

  • Swift编程中用以管理内存的自动引用计数详解

    Swift 内存管理功能是通过使用自动引用计数(ARC)来处理.ARC用于初始化和取消初始化所述系统资源,从而释放使用的类实例的存储器空间当实例不再需要.ARC跟踪代码的实例有效地管理存储资源之间的关系的信息. ARC的功能 在每一次一个新的类实例被创建时ARC分配一块内存以存储信息 init() 关于实例类型和其值的信息存储在存储器中 当类实例不再需要它自动由 deinit() 释放,用于进一步类实例的存储和检索的存储空间 ARC保存在磁道当前参照类实例的属性,常量和变量,使得 deinit(

  • 详解 swift3.0 可选绑定共用同一块内存空间的实例

    详解 swift3.0 可选绑定共用同一块内存空间的实例 示例代码: ljTempModel = UserModel.init(userName: "sww", userID: 12, phone: "123", email: "deew") ljTempModel?.ljArray.append("sww") print("可选绑定前:\(ljTempModel?.ljArray)") //可选绑定成功,

  • 深入讲解Swift的内存管理

    前言 LLVM编译器的好:Swift的内存管理除了要注意引用循环之外,几乎全部被LLVM编译器包揽,不需要开发人员操心. Swift 是自动管理内存的,这也就是说,我们不再需要操心内存的申请和分配.当我们通过初始化创建一个对象时,Swift 会替我们管理和分配内存.而释放的原则遵循了自动引用计数 (ARC) 的规则:当一个对象没有引用的时候,其内存将会被自动回收.这套机制从很大程度上简化了我们的编码,我们只需要保证在合适的时候将引用置空 (比如超过作用域,或者手动设为 nil 等),就可以确保内

  • 详解Swift的内存管理

    内存管理 和OC一样, 在Swift中也是采用基于引用计数的ARC内存管理方案(针对堆空间的内存管理) 在Swift的ARC中有三种引用 强引用(strong reference):默认情况下,代码中涉及到的引用都是强引用 弱引用(weak reference):通过weak定义弱引用 无主引用(unowned reference):通过unowned定义无主引用 weak 弱引用(weak reference):通过weak定义弱引用必须是可选类型的var,因为实例销毁后,ARC会自动将弱引用

  • Swift 列举内存管理与异常处理具体代码

    1. Swift 内存销毁时机 // Swift5 内存销毁时机 // 引用类型的内存销毁时机 class ClassDemo { var a = "value a" deinit { // 实例被释放 print("deinit class a") } } // 可空类型 var ins1: ClassDemo? = ClassDemo() var ins2 = ins1 var ins3 = ins2 ins1 = nil // 取消 ins1 引用 ins2

  • C#内存管理CLR深入讲解(下篇)

    <上篇>中我们主要讨论的是程序集(Assembly)和应用程序域(AppDomain)的话题,着重介绍了两个不同的程序集加载方式——独占方式和共享方式(中立域方式):以及基于进程范围内的字符串驻留.这篇将关注点放在托管对象创建时内存的分配和对大对象(LO:Large Object)的回收上,不对之处,还望各位能够及时指出. 一.从类型(Type)与实例(Instance)谈起 在面向对象的世界中,类型和实例是两个核心的要素.不论是类型和实例,相关的信息比如加载到内存中,对应着某一块或者多块连续

  • C#内存管理CLR深入讲解(上篇)

    半年之前,PM让我在部门内部进行一次关于“内存泄露”的专题分享,我为此准备了一份PPT.今天无意中将其翻出来,觉得里面提到的关于CLR下关于内存管理部分的内存还有点意思.为此,今天按照PPT的内容写了一篇文章.本篇文章不会在讨论那些我们熟悉的话题,比如“值类型引用类型具有怎样的区别?”.“垃圾回收分为几个步骤?”.“Finalizer和Dispose有何不同”.等等,而是讨论一些不同的内容.整篇文章分上下两篇,上篇主要谈论的是“程序集(Assembly)和应用程序域(AppDomain)”.也许

  • C语言深入细致讲解动态内存管理

    目录 为什么存在动态内存管理 动态内存函数的介绍 malloc free calloc realloc 常见的动态内存错误 对NULL指针的解引用操作 对动态开辟空间的越界访问 对非动态开辟内存使用free访问 使用free 释放一块动态开辟内存的一部分 对一块动态内存多次释放 对动态内存开辟忘记释放 柔性数组 小结 为什么存在动态内存管理 我们已经掌握的内存开辟方式有: int val = 20;//在栈空间上开辟四个字节 char arr[10] = { 0 };//在栈空间上开辟10个字节

  • C语言详细分析讲解内存管理malloc realloc free calloc函数的使用

    目录 C语言内存管理 一.动态空间申请 二.动态空间的扩容 三.释放内存 C语言内存管理 malloc && realloc && free && calloc c语言中为了进行动态内存管理,<stdlib.h>中提供了几个函数帮助进行内存管理. 我们知道,C语言中是没有C++中的容器或者说是python中list,set这些高级的数据结构的,我们一旦申请了一段内存空间以后这一段空间就归你了,比如我们举个例子,我们申请一个数组 int nums[

  • C++全面覆盖内存管理知识讲解

    目录 前言 一.C++内存管理方式 1.1new/delete操作内置类型 二.operator new与operator delete函数 2.1operator new与operator delete函数 二.new和delete的实现原理 2.1内置类型 2.2 自定义类型 前言 C语言内存管理方式在C++中可以继续使用,但有些地方就无能为力而且使用起来比较麻烦,因此C++又提出了自己的内存管理方式:通过new和delete操作符进行动态内存管理. 一.C++内存管理方式 1.1new/d

  • C++详细讲解内存管理工具primitives

    目录 primitives new 和 delete placement new 重载 operator new per-class allocator New Handler =default,=delete primitives 分配 释放 属于 是否可重载 malloc() free() C 不可 new delete C++表达式 不可 ::operator new() ::operator delete() C++函数 可 allocator::allocate() allocator

随机推荐