Swift 内存管理详解

Swift内存管理:

Swift 和 OC 用的都是ARC的内存管理机制,它们通过 ARC 可以很好的管理对象的回收,大部分的时候,程序猿无需关心 Swift 对象的回收。

    注意:

只有引用类型变量所引用的对象才需要使用引用计数器进行管理,对于枚举、结构体等,他们都是值类型的。因此不需要使用引用计数进行管理。

一:理解ARC

    1: ARC 自动统计改对象被多少引用变量引用,这个值就是我们常说的引用计数器。

    2: 每当引用计数器计数变为0的时候,ARC就会回收这个对象。

    比如,现在我们做一个针对大学生用户的APP,我们写了一个User类,这个类里面有姓名、年纪、班级三个属性,看整个文件代码:

import UIKit
class ComNavigationController: UINavigationController {

      class User {
        
        var name:String
        var age:Int
        var classes:String
        init(name:String,age:Int,classes:String)
        {
          self.name = name
          self.age = age
          self.classes = classes
        }
    
        deinit{
            print("\(self.name) 用户即将被销毁")
        }
    }
    override func viewDidLoad() {
       
       var user1:User?
        user1 = User(name:"zhangxu",age:24,classes:"三年二班")
        /// 创建了一个User对象  用户1这个变量是指向User对象的,这时候User对象的引用计数为1
        
        var user2:User?
        user2 = user1
        
        var user3:User?
        user3 = user1
        // 这时候被变量2 和变量3  都引用了,User对象的引用计数就变成了------ 3 
        
        print(user2)
        print(user3)
        
        user1 = nil
        user2 = nil
        user3 = nil
        // 1 2 3 都置为你了  用户都不再引用User对象
        // 这时候 User对象不被任何变量引用,引用计数器就变成了0
        //  引用计数器编程了 0 ,ARC就会回收该对象
        
        super.viewDidLoad()
        // Do any additional setup after loading the view.
    }
    
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
    /*
    // MARK: - Navigation

    // In a storyboard-based application, you will often want to do a little preparation before navigation
    override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
        // Get the new view controller using segue.destinationViewController.
        // Pass the selected object to the new view controller.
    }
    */
}

二:强引用循环

大部分时候,ARC能够很好的处理程序中对象的内存回收,但如果这两个对象之间存在着相互的引用,也就是当两个对象都使用存储属性相互的引用对方的时候,此时两个对象的引用计数都等于 1 ,但实际上它们都没有被真正的引用变量所引用,就像上面的 user1 这样的变量。这时候的 ARC是无法回收它们的。

     看下面的代码示例:

    class teacher {
        
        var name:String
        var age:Int
        var student1:student?
        
        init(name:String,age:Int)
        {
          self.name = name
          self.age = age
        }
        deinit{
        
            print("老师对象被回收");
        }
        
    }
    
    class student {
        
        var name:String
        var age:Int
        var teacher1:teacher?
        init(name:String,age:Int)
        {
            self.name = name
            self.age = age
        }
        deinit{
            
            print("老师对象被回收");
        }
        
    }

     var stu:student? = student(name: "zhangxiaxu",age: 24)
     var tea:teacher? = teacher(name: "wangnima",age: 200)
        
     // 就在这里相互引用,形成了强引用循环
     stu?.teacher1 = tea
     tea?.student1 = stu
        
     stu = nil
     tea = nil      

 解释一下:

     上面的代码执行完之后,两个对象之间不再有真正的引用变量引用他们,但两个对象之间的相互引用,形成了"强引用循环",此时它们的引用计数为 1 ,ARC也不会去回收它们,任何一个对象释放,都要等对方先释放,因此两个对象只爱你谁都没办法被回收,这两个对象在这时候就变成了垃圾。为了结局上面的强引用循环,我们就必须让一方先放手,允许对方先释放。Swift这时候提供了两种机制: 弱引用和无主引用

三:使用弱引用解决强引用循环

弱引用不会增加对方的引用计数,因此不会阻止ARC回收被引用的实例,这样就避免了形成强引用循环, 在定义属性的 var 关键字之前加 weak 就定义了弱引用。

     注意点:

     1 : 弱引用变量要求该变量必须要能被设置成 nil ,也就是弱引用的属性最好是使用可选类型来定义。

     2 : 弱引用的属性只能声明为变量类型,因为该属性在运行期内只有可能会发生变化,因此不能设置成常量。

     3 :也没必要把两个相互引用的属性都设置成弱引用,有一个就可以了。

     所以,要是使用弱引用解决上面的强引用循环的,只需按下面声明属性:

// 修改teacher类的 student 为弱引用属性       
 weak var student1:student?
       
// 或者修改 student 类的 teacher 为弱引用属性
 weak var teacher1:teacher?

四:使用无主引用解决强引用循环

 与弱引用相似的是,无主引用也不会增加对方的引用计数,无主引用于弱引用的区别:

     无主引用不允许接受nil,意思就是这个属性要一直有值!因此无主引用只能定义为非可选类型。

     在定义属性 var 或者 let 之前,添加 unowned 关键字即可。上面的强引用要用无主引用解决的话,看下面代码:

// 声明 teacher 类的 student 属性为无主引用 且 不能是可选类型。 
 unowned let student1:student

// 或者声明 student 类的 teacher 属性为无主引用
 unowned let teacher1:teacher

 五:闭包的强引用循环解决

上面给出了两种方式,说说他们的使用场景的一个区别。

      当闭包和捕获的对象总是相互引用,并且总是同事销毁时,应该将闭包内捕获的实例定义为无主引用。

      当闭包捕获的引用变量有可能是 nil 时,将闭包捕获的引用变量定义为弱引用。

      如果程序将该对象本身传入了闭包,那么闭包本身就会捕获该对象,于是该对象就持有了闭包属性,反过来,闭包也持有对象,这样子就形成了强引用。

import UIKit
class ComNavigationController: UINavigationController {
    
    class teacher {
        
        var name:String
        var age:Int
        
        lazy var findteacher:() ->String = {
           
            [unowned self] in
            return  "该老师名字是\(self.name)  年纪是\(self.age)"
           
            //[weak self] in
            //return  "该老师名字是\(self!.name)  年纪是\(self!.age)"
        }
        
        init(name:String,age:Int)
        {
          self.name = name
          self.age = age
        }
        
        deinit{
        
            print("老师对象被回收");
        }
    }
    
    override func viewDidLoad() {
    
        super.viewDidLoad()
        
        var  tea:teacher? = teacher(name: "葫芦娃",age: 24)
        var find:(() ->String)? = tea!.findteacher
        
        tea = nil
        find = nil

        // Do any additional setup after loading the view.
    }
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
}

解释一下:

    上面代码中,我们看在 viewdidload 方法中,先创建了一个 teacher 对象,并且赋值给 tea 变量,接下来有定义了一个函数类型的变量,并且将 teacher 实例的 findteacher 属性赋值给该变量,到后面tea 和 find 变量都赋值为 nil , 此时没有引用变量引用 teacher 对象和闭包对象,但两个对象之间的相互引用就形成了强引用循环。 

    当然,我们只是说形成了,上面的代码里面也已经给出了解决的方法,尤其注意一点,就是使用无主引用和弱引用时候 self 的区别。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏积累沉淀

JavaScript对象和数组

学习要点: 1.Object类型 2.Array类型 3.对象中的方法 什么是对象,其实就是一种类型,即引用类型。而对象的值就是引用类型的实例。 一...

2765
来自专栏mukekeheart的iOS之旅

OC学习5——类和对象

1、OC是在C语言基础上进行扩展得到的一门面向对象的程序设计语言,它也提供了定义类、成员变量和方法的基本功能。类可以被认为是一种自定义的数据类型,使用它可以定义...

2826
来自专栏C语言及其他语言

【编程经验】结构体的高级使用及共用体的定义和使用

结构体数组 结构体数组是一个数组,其数组的每一个元素都是结构体类型。在实际应用中,经常用结构体数组来 表示具有相同数据结构的一个群体,如一个班的学生档案,...

35611
来自专栏前端知识分享

javascript易混淆的split()、splice()、slice()方法详解

很多时候,一门语言总有那么些相似的方法,容易让人傻傻分不清楚,尤其在不经常用的时候。而本文主要简单总结了JavaScript中的关于字符串和数组中三个容易混淆的...

1362
来自专栏从流域到海域

《Java程序设计基础》 第8章手记Part 2

第八章内容 Part 2 - … - 抽象类和抽象方法 - 接口及接口的实现 - 利用接口实现类的多重继承 - 内部库和匿名类 ...

2179
来自专栏和蔼的张星的图像处理专栏

423. 有效的括号序列利用堆栈

给定一个字符串所表示的括号序列,包含以下字符: '(', ')', '{', '}', '[' and ']', 判定是否是有效的括号序列。

1146
来自专栏彭湖湾的编程世界

【javascript】您好, 您要的ECMAScript6速记套餐到了 (一)

【前言】本文“严重参考” 自阮一峰老师写的ES6文档,在此我郑重感谢他沉默无声的帮助 总结一下ES6为 javascript中的 对象/数组/函数 这JS三巨头...

1947
来自专栏微信公众号:Java团长

Java基础01 从HelloWorld到面向对象

Java是完全面向对象的语言。Java通过虚拟机的运行机制,实现“跨平台”的理念。我在这里想要呈现一个适合初学者的教程,希望对大家有用。

981
来自专栏极客猴

Python中“is”和“==”的区别

相比 C/C++ 、Java 等强类型语言, Python 定义变量的方式就简单多了。我们只需要给变量起个变量名,而不需要给变量指定类型。

1042
来自专栏LanceToBigData

JavaSE(一)之类与对象

终于到了要学习面向对象程序设计了,其中可能很多东西以前都知道怎么去用,但是却不知道怎么来的,或者怎么样写会出错,所以今天总结起来。 一、OOP概述   Java...

2215

扫码关注云+社区

领取腾讯云代金券