实例讲解Swift中引用类型的ARC自动引用计数_Swift

一、引言

ARC(自动引用计数)是Objective-C和Swift中用于解决内存管理问题的方案。在学习Objective-C编程时经常会学习到一个关于ARC的例子:在一个公用的图书馆中,每次进入一人就将卡插入,走的时候将自己的卡拔出拿走。图书馆系统会判定只要有卡插入,就将图书馆的灯打开,当所有卡都被取走后,将图书馆的灯关掉。这个例子对应于Objective-C中的对象声明周期管理十分贴切。每当一个对象增加一个引用时,其引用计数会加1,当一个引用被取消时,对象的引用计数减1,当引用计数减为0时,说明此对象将不再有任何引用,对象会被释放掉,让出内存。Swift也采用同样的方式进行内存管理。

注意:在Swift中只有引用类型有自动引用计数,结构体、枚举这类值类型是没有引用计数的。关于引用计数的示例代码如下:

class MyClass {
  deinit{
    print("MyClass deinit")
  }
}
var cls1:MyClass? = MyClass()
var cls2:MyClass? = cls1
var cls3:MyClass? = cls2
cls2 = nil
cls1 = nil
//执行下面代码后才会打印“MyClass deinit”
cls3 = nil

二、循环引用的处理方法

在开发中,开发者一不小心就会写出产生循环引用的代码,在上面的示例中可以看出,除非实例的引用全部解除,否则实例将不会调用析构方法,内存不会被释放,如果在写代码时,A引用了B,同样B也引用了A,那么实际上现在A和B的引用计数都是2,将A和B都置为nil后,A和B实例依然保有1个引用计数,都不会被释放,实例如下:

class MyClassOne {
  var cls:MyClassTwo?
  deinit{
    print("ClassOne deinit")
  }
}
class MyClassTwo {
  var cls:MyClassOne?
  deinit{
    print("ClassTwo deinit")
  }
}
var obj1:MyClassOne? = MyClassOne()
var obj2:MyClassTwo? = MyClassTwo()
obj1?.cls = obj2
obj2?.cls = obj1
obj1=nil
obj2=nil
//没有打印析构函数的调用信息

对于上面的情况,可以将属性声明称weak类型来防止这种循环引用,weak的作用在于只是弱引用实例,原实例的引用计数并不会加1,示例如下:

//关于弱引用的演示
class MyClassThree{
  weak var cls:MyClassFour?
  deinit{
    print("ClassThree deinit")
  }
}
class MyClassFour {
  var cls:MyClassThree?
  deinit{
    print("ClassFour deinit")
  }
}
var obj3:MyClassThree? = MyClassThree()
var obj4:MyClassFour? = MyClassFour()
obj3?.cls = obj4
obj4?.cls = obj3
obj4=nil
//此时obj3中的cls也为nil
obj3?.cls

若引用的实例被释放后,其在另一个实例中的引用也将被置为nil,所以weak只能用于optional类型的属性,然而在开发中还有一种情况,某个类必须保有另一个类的示例,这个实例不能为nil,但是这个属性又不能影响其原始实例的释放,这种情况也会造成循环引用,示例如下:

class MyClassFive{
  var cls:MyClassSix
  init(param:MyClassSix){
    cls = param
  }
  deinit{
    print("ClassFive deinit")
  }
}
class MyClassSix{
  var cls:MyClassFive?
  deinit{
    print("ClassSix deinit")
  }
}
var obj6:MyClassSix? = MyClassSix()
var obj5:MyClassFive? = MyClassFive(param: obj6!)
obj6?.cls = obj5
obj5=nil
obj6=nil
//没有打印任何信息

上面的示例也会造成循环引用,然而MyClassFive类中的cls属性为常量不可为nil,不可使用weak弱引用来做Swift中又提供了一个关键字unowned无主引用来处理这样的问题,示例如下:

class MyClassFive{
  unowned var cls:MyClassSix
  init(param:MyClassSix){
    cls = param
  }
  deinit{
    print("ClassFive deinit")
  }
}
class MyClassSix{
  var cls:MyClassFive?
  deinit{
    print("ClassSix deinit")
  }
}
var obj6:MyClassSix? = MyClassSix()
var obj5:MyClassFive? = MyClassFive(param: obj6!)
obj6?.cls = obj5
obj5=nil
obj6=nil

关于弱引用和无主引用,其区别主要是在于:

1.弱引用用于解决Optional值的引起的循环引用。

2.无主引用用于解决非Optional值引起的循环引用。

3.个人以为,弱引用可用下图表示:

4.无主引用可用如下图表示:

若将上面的代码修改如下,程序会直接崩溃:

class MyClassFive{
  unowned var cls:MyClassSix
  init(param:MyClassSix){
    cls = param
  }
  deinit{
    print("ClassFive deinit")
  }
}
class MyClassSix{
  var cls:MyClassFive?
  deinit{
    print("ClassSix deinit")
  }
}
var obj6:MyClassSix? = MyClassSix()
var obj5:MyClassFive? = MyClassFive(param: obj6!)
obj6?.cls = obj5
obj6=nil
obj5?.cls

上面所举的例子满足了两种情况,一种是两类实例引用的属性都是Optional值的时候使用weak来解决循环引用,一种是两类实例有一个为非Optional值的时候使用unowned来解决循环引用,然而还有第三种情况,两类实例引用的属性都为非Optional值的时候,可以使用无主引用与隐式拆包结合的方式来解决,这也是无主引用最大的应用之处,示例如下:

class MyClassSeven{
  unowned var cls:MyClassEight
  init(param:MyClassEight){
    cls = param
  }
  deinit{
    print("ClassSeven deinit")
  }
}
class MyClassEight{
  var cls:MyClassSeven!
  init(){
    cls = MyClassSeven(param:self)
  }
  deinit{
    print("ClassEight deinit")
  }
}
var obj7:MyClassEight? = MyClassEight()
obj7=nil

除了在两个类实例间会产生循环引用,在闭包中,也可能出现循环引用,当某个类中包含一个闭包属性,同时这个闭包属性中又使用了类实例,则会产生循环引用,示例如下:

class MyClassNine {
  var name:String = "HS"
  lazy var closure:()->Void = {
    //闭包中使用引用值会使引用+1
    print(self.name)
  }
  deinit{
    print("ClassNine deinit")
  }
}
var obj9:MyClassNine? = MyClassNine()
obj9?.closure()
obj9=nil
//不会打印析构信息

Swift中提供了闭包的捕获列表来对引用类型进行弱引用或者无主引用的转换:

class MyClassNine {
  var name:String = "HS"
  lazy var closure:()->Void = {
    [unowned self]()->Void in
    print(self.name)
  }
  deinit{
    print("ClassNine deinit")
  }
}
var obj9:MyClassNine? = MyClassNine()
obj9?.closure()
obj9=nil

捕获列表以中括号标识,多个捕获参数则使用逗号分隔。

以上是小编为您精心准备的的内容,在的博客、问答、公众号、人物、课程等栏目也有的相关内容,欢迎继续使用右上角搜索按钮进行搜索引用
, arc
, swift
自动引用计数
swift 引用计数、ios arc 查看引用计数、arc 打印引用计数、arc 查看引用计数、arc 引用计数,以便于您获取更多的相关知识。

时间: 2024-09-20 00:12:51

实例讲解Swift中引用类型的ARC自动引用计数_Swift的相关文章

obj-c编程11:内存管理和ARC(自动引用计数)

    乖乖隆地洞,这篇文章内容可是不得了,内存管理哦!首先,这个要是搞不明白,你就等着进程莫名其妙的挂死,或是疯狂申请内存却不释放,结果被OS杀死,不管是"自杀"还是"他杀",都不是那么好玩的哦.其次要记住这可不是windows 中的内存管理(Win32 api),也不是linux中C like的内存管理方法.这个比他们都"高级"的多啊!但是没有ruby的高级,也没有ruby的简单,如果mac编程用ruby的就好了,这不搞出一个雨燕(SWFIT

【iOS7的一些总结】1、ARC自动引用计数

对于软件开发而言,引用计数Reference Counting不是一个陌生的概念.在组件对象模型COM中,这就是一个非常重要的概念.每一个对象都维持着一个量称为"引用计数",标志着有多少"客户"程序在引用当前的对象.只要还有"客户"引用当前对象,也就是引用计数非零,那么这个对象将会保存在内存中不会消失:如果没有任何"客户"引用当前对象了,那引用计数将被设为0,此时该对象将会从内存中释放.通过这种机制可以防止已经废弃的对象继续存

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

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

[SMS&WAP]实例讲解制作OTA短信来自动配置手机WAP书签[附源码]

[SMS&WAP]实例讲解制作OTA短信来自动配置手机WAP书签 编写者 日期 关键词 郑昀@ultrapower 2005-9-5 Sms wap ota 书签 空中下载手机上网设置 WDP WSP WBXML 源代码:otasms.rar  (不能下载的话,请来信) 空中下载(OTA)的概念 OTA,即Over The Air,国内翻译为空中下载. OTA标准由爱立信和诺基亚共同制订.OTA涵盖了许多范围,比如Kjava中的应用程序下载也是通过OTA.我们这篇文章主要讲的是,通过短信方式空中

实例讲解设计模式中的命令模式在iOS App开发中的运用_IOS

命令模式封装一个请求或行为作为一个对象.封装的请求比原的更加灵活,可以在对象之间传递,储存,动态修改,或放入一个队列. 那么让我们简要的说一下命令模式的特点. 它能比较容易地设计一个命令队列: 在需要的情况下,可以较容易地将命令记入日志: 允许接收请求地一方决定是否要否决请求: 可以容易地实现对请求地撤销和重做: 由于加进新地具体命令类不影响其他的类,因此增加新的具体命令类很容易: 把请求一个操作的对象与知道怎么执行一个操作的对象分隔开. 下面给出基本的类结构图: 上面这张图是命令模式的类结构的

实例讲解JS中setTimeout()的用法_javascript技巧

本文实例讲解了JS中setTimeout()的用法,分享给大家供大家参考,具体内容如下 效果图: 具体代码: <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=gb2312" /> <title>无标题文档</title> <script type="text/javascript"&

实例讲解JavaScript中call、apply、bind方法的异同_javascript技巧

以实例切入,讲解JavaScript中call,apply,bind方法,供大家参考,具体内容如下 <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title></title> <script type="text/javascript"> function MAN(name, sex, age) { this.name =

实例讲解jQuery中对事件的命名空间的运用_jquery

用 jQuery 绑定和解绑事件监听器都是非常简单的.但是当你为一个元素的一个事件绑定了多个监听器时,怎样精确地解绑其中一个监听器?我们需要了解一下事件的命名空间. 看下面这段代码: $("#element") .on("click", doSomething) .on("click", doSomethingElse); 像上面这样绑定事件监听器,当元素被点击时,doSomething 和 doSomethingElse 这两个监听器都会被触发

在Swift中使用Cocoa的现有设计模式介绍_Swift

使用 Cocoa 现有的一些设计模式,是帮助开发者开发一款拥有合理设计思路.稳定的性能.良好的可扩展性应用的有效方法之一.这些模式都依赖于在 Objective-C 中定义的类.因为 Swift 与 Objective-C 的互用性,所以你依然可以在 Swift 代码中使用这些设计模式.在一些情况下,你甚至可以使用 Swift 语言的特性扩展或简化这些 Cocoa 设计模式,使这些设计模式更强大.更易于使用. 委托(Delegation) 在 Swift 和 Objective-C 中,委托通常