美文网首页Swift开发技巧
swift 自动引用计数

swift 自动引用计数

作者: 皆为序幕_ | 来源:发表于2019-05-29 15:42 被阅读0次

Swift 使用自动引用计数(ARC)机制管理你的应用程序的内存。通常情况下,Swift 内存管理机制会一直起作用,你无须自己来考虑内存的管理。ARC 会在类的实例不再被使用时,自动释放其占用的内存

引用计数仅仅应用于类的实例。结构体和枚举类型是值类型,不适应


自动引用计数(表示对象被引用的次数)

1、每创建一个类的实例对象,ARC就会分配一块内存来存储实例信息(实例的类型信息及实例的存储属性)
2、当实例不再被使用时,ARC 释放实例所占用的内存,这确保了不再被使用的实例,不会一直占用内存空间
3、当 ARC 释放了正在被使用中的实例,该实例的属性和方法将不能再被访问和调用。实际上,如果你试图访问这个实例,你的应用程序很可能会崩溃
4、为了确保使用中的实例不会被销毁,ARC 会跟踪和计算每一个实例正在被多少属性,常量和变量所引用。哪怕实例的引用数为1,ARC都不会销毁这个实例

class Student{
    let name:String
    init(name:String) {
        self.name = name
        print("init")
    }
    deinit {
        print("deinit")
    }
}

var stu0:Student?        //nil
var stu1:Student?        //nil
var stu2:Student?       //nil

stu0 = Student.init(name: "lilei")  //打印log:init
stu1 = stu0
stu2 = stu0
stu0 = nil
stu1 = nil
stu2 = nil    //打印log:deinit

类实例之间的循环强引用

  • 两个类实例都持有一个强引用的指向对方的属性,这就是所谓的循环强引用
  • 类之间的关系用弱引用替代强引用,从而解决循环强引用的问题


    循环引用
class ClassA{
    
    let aStr:String
    var b:ClassB?

    init(str:String) {
        self.aStr = str
    }
    deinit {
        print("ClassA释放")
    }
}

class ClassB{
    
    let bStr:String
    var a:ClassA?

    init(str:String) {
        self.bStr = str
    }
    deinit {
        print("ClassB释放")
    }
}

var objA:ClassA?
var objB:ClassB?

objA = ClassA(str: "A")
objB = ClassB(str: "B")
objA!.b = objB
objB!.a = objA

objA = nil
objB = nil
解决类之间循环引用
  • 弱引用:不会保持所引用的实例
    • 声明属性或者变量时,在前面加上weak关键字表明这是一个弱引用
    • ARC 会在引用的实例被销毁后自动将其赋值为nil


      Snip20190523_7.png
class ClassA{
    
    let aStr:String
    var b:ClassB?

    init(str:String) {
        self.aStr = str
    }
    deinit {
        print("ClassA释放")    //正常打印
    }
}

class ClassB{
    
    let bStr:String
    weak var a:ClassA?

    init(str:String) {
        self.bStr = str
    }
    deinit {
        print("ClassB释放")   //正常打印
    }
}

var objA:ClassA?
var objB:ClassB?

objA = ClassA(str: "A")
objB = ClassB(str: "B")
objA!.b = objB
objB!.a = objA

objA = nil
objB = nil
  • 无主引用:无主引用修饰的实例属性与引用它的实例有着相同的生命周期
    • 在声明属性或者变量时,在前面加上关键字unowned表示这是一个无主引用
    • 使用无主引用,必须确保引用始终指向一个未销毁的实例
    • 如果试图在实例被销毁后,访问该实例的无主引用,会触发运行时错误


      Snip20190523_8.png
class ClassA{
    
    let aStr:String
    var b:ClassB?

    init(str:String) {
        self.aStr = str
    }
    deinit {
        print("ClassA释放")   //正常打印
    }
}

class ClassB{
    
    let bStr:String
    unowned var a:ClassA?

    init(str:String) {
        self.bStr = str
    }
    deinit {
        print("ClassB释放")   //正常打印
    }
}

var objA:ClassA?
objA = ClassA(str: "A")
objA!.b = ClassB(str: "B")
objA = nil  //当objA释放后,那么ClassB也被释放

闭包引起的循环强引用

将一个闭包赋值给类实例的某个属性,并且这个闭包体中又使用了这个类实例时。这个闭包体中可能访问了实例的某个属性,例如self.someProperty,或者闭包中调用了实例的某个方法,例如self.someMethod()。这两种情况都导致了闭包“捕获”self,从而产生了循环强引用

闭包引起的循环强引用
class ClassA{
 
    let strA: String
    let showValue:Bool

    //定义了一个lazy属性closures,这个属性引用了strA的闭包,该属性是Void -> String类型
    //默认情况下,闭包赋值给了closures属性,这个闭包返回一个字符串

    lazy var closures: () -> String = {
        
        if self.showValue {
            return self.strA
        } else {
            return "空空如也"
        }
    }
    
    init(str:String, show:Bool) {
        self.strA = str
        self.showValue = show
    }
    deinit {
        print("A释放")
    }
}

var objA:ClassA?
objA = ClassA.init(str: "AA",show: true)
var log:String = objA!.closures()
print(log)
objA = nil

//打印:A释放
解决闭包引起的循环强引用

在定义闭包的时候,再定义捕获列表作为闭包的一部分,捕获列表定义了闭包体内捕获一个或者多个引用类型的规则。跟解决两个类实例间的循环强引用一样,声明每个捕获的引用为弱引用或无主引用

  • 定义捕获列表
    捕获列表中的每一项都由一对元素组成,一个元素是weakunowned关键字,另一个元素是类实例的引用(例如self)或初始化过的变量(如delegate = self.delegate!),这些项在方括号中用逗号分开

    • 如果闭包有参数列表和返回类型,把捕获列表放在它们前面
    lazy var someClosure: (Int, String) -> String = {
        [unowned self, weak delegate = self.delegate!] (index: Int, stringToProcess: String) ->   String in
        // 这里是闭包的函数体
    }
    
    • 如果闭包没有指明参数列表或者返回类型,即它们会通过上下文推断,那么可以把捕获列表和关键字in放在闭包最开始的地方
    lazy var someClosure: Void -> String = {
        [unowned self, weak delegate = self.delegate!] in
    // 这里是闭包的函数体
    }
    
  • 弱引用:在被捕获的引用可能会变为nil时,将闭包内的捕获定义为弱引用

  • 无主引用 :在闭包和捕获的实例总是互相引用并且总是同时销毁时,将闭包内的捕获定义为无主引用

  • 如果被捕获的引用绝对不会变为nil,应该用无主引用,而不是弱引用


    解决闭包引起的循环强引用
class ClassA{
 
    let strA: String
    let showValue:Bool
    //定义了一个lazy属性closures,这个属性引用了strA的闭包,该属性是Void -> String类型
    //默认情况下,闭包赋值给了closures属性,这个闭包返回一个字符串
    lazy var closures: () -> String = {
          
        //捕获列表是[unowned self],表示“将self捕获为无主引用而不是强引用”
        [unowned self]   in
       
        if self.showValue {
            return self.strA
        } else {
            return "空空如也"
        }
    }
    
    init(str:String, show:Bool) {
        self.strA = str
        self.showValue = show
    }
    deinit {
        print("A释放")
    }
}

var objA:ClassA?
objA = ClassA.init(str: "AA",show: true)
var log:String = objA!.closures()
print(log)
objA = nil

打印:
AA
A释放

相关文章

  • 【官方文档翻译】Swift4.0.3 ☞ Automatic R

    原文链接 自动引用计数(Automatic Reference Counting) Swift 使用 自动引用计数...

  • Swift自动引用计数

    自动引用计数(Automatic Reference Counting) Swift 使用自动引用计数(ARC)机...

  • Swift:基础(二十七)自动引用计数

    Swift 自动引用计数(ARC) Swift 使用自动引用计数(ARC)这一机制来跟踪和管理应用程序的内存 通常...

  • 引用计数

    自动引用计数 Swift 使用自动引用计数(ARC)机制来跟踪和管理你的应用程序的内存 自动引用计数的工作机制 每...

  • Swift语法(五)

    一.自动引用计数 1.自动引用计数工作机制 1.1 swift和oc一样,采用自动引用计数来管理内存 1.2 当有...

  • Swift : 内存泄露原因及解决办法

    Swift 自动引用计数(ARC) 在Swift中 使用自动引用计数(ARC)这一机制来跟踪和管理应用程序的内存 ...

  • Swift3.0-自动引用计数

    自动引用计数 Swift 使用自动引用计数(ARC)机制来跟踪和管理你的应用程序的内存。通常情况下,Swift 内...

  • Swift - 自动引用计数

    自动引用计数 Swift 使用自动引用计数(ARC)机制来跟踪和管理你的应用程序的内存。通常情况下,Swift 内...

  • Swift基础10

    自动引用计数 swift使用自动引用计数(ARC)机制来跟踪和管理你的应用程序的内存。通常情况下,Swift内存管...

  • 第十六章 自动引用计数

    swift用自动引用计数(ARC)机制来对引用类型进行内存管理,(包括类实例和闭包)。 自动引用计数会引入循环强引...

网友评论

    本文标题:swift 自动引用计数

    本文链接:https://www.haomeiwen.com/subject/emilfqtx.html