美文网首页
循环强引用

循环强引用

作者: BetterComingDay | 来源:发表于2017-06-28 15:02 被阅读21次

循环强引用会导致内存泄漏
内存泄漏(Memory Leak)是指程序中己动态分配的堆内存由于某种原因程序未释放或无法释放,造成系统内存的浪费,导致程序运行速度减慢甚至系统崩溃等严重后果
swift给出两种解除循环引用的方式,弱引用weak 和无主引用unowned

一、类之间互相持有会引发强引用
class Teacher{
    var student:Student?
    deinit {
        print("Teacher被销毁了")
    }
}

class Student{
    var teacher:Teacher?
    deinit {
        print("Student被销毁了")
    }
}
var tea:Teacher?
var stu:Student?
tea = Teacher()
stu = Student()
tea?.student = stu
stu?.teacher = tea
tea = nil
stu = nil

如上所示,tea、stu置为nil之后,控制台也没有输出任何信息,证明两个对象都没有被销毁,造成了内存泄漏。
解决办法如下:在任一类里边引用属性前添加weak即可

class Teacher{
    var student:Student?
    deinit {
        print("Teacher被销毁了")
    }
}

class Student{
    weak var teacher:Teacher?
    deinit {
        print("Student被销毁了")
    }
}
var tea:Teacher?
var stu:Student?
tea = Teacher()
stu = Student()
tea?.student = stu
stu?.teacher = tea
tea = nil
stu = nil

控制台:

Teacher被销毁了
Student被销毁了

其实上边提到的解决循环引用的方法,可以使用弱引用weak,也可以使用无主引用unwoned。
unowned与weak的区别是,无主引用无法在实例被销毁后被设置为nil,这时候被访问就会触发运行时错误。
倘若你使用 weak,属性可以是可选类型,即允许有 nil 值的情况。另一方面,倘若你使用 unowned,它不允许设为可选类型。因为一个 unowned 属性不能为可选类型,
根据属性是否为可选类型,你可以在 weak 和 unowned 之间进行选择。

tips:

  • 当两个实例属性都允许为nil时,适合弱引用;
  • 当两个实例属性一个允许为nil,另一个不允许为nil时,适合无主引用;
  • 当两个实例属性都不允许为nil时,这时候为了解决两个实例之间的循环引用,就需要一个类使用无主引用,另一个类使用隐式解析可选属性,具体如下:
    首先需要明确一点,可选类型使用的时候需要解包,隐式可选类型使用的时候不需要解包,详见Optional可选类型
class City{
    let name:String
    var province:Province!
    init(cityName:String, provinceName:String){
        self.name = cityName
        self.province = Province(name:provinceName, city:self)
    }
    deinit {
        print("City Destoryed")
    }
}

class Province{
    let name:String
    unowned var city:City
    init(name: String, city:City){
        self.name = name
        self.city = city
    }
    deinit {
        print("Province Destoryed")
    }
}

var city:City? = City(cityName:"石家庄" ,provinceName:"河北")
print(city!.province.name)
city = nil

控制台:

河北
City Destoryed
Province Destoryed
本人的疑问:

1、为什么要声明为隐式可选类型?
排除法:

  • 声明为非可选类型var province:Province
    系统会报错,详细信息见图片,是因为self的存储属性还没有初始化完成,你就使用self当做参数,所以报错。
    所以需要声明为隐式可选类型,这样意味着province有默认值nil。调用self当做参数就不会报错。
Snip20170628_2.png
  • 声明为可选类型?
    如下图,既然已经确定province有值,再声明成可选类型的话就会造成代码冗余,最明显的就是空合运算符那里,根本就不会走到。


    Snip20170628_5.png
二、闭包可能引起循环强引用

造成强引用的原因是闭包赋值给类的属性,同时闭包内部引用了这个类的实例,跟OC里边的block循环引用是一样一样的。
看下边例子:

class Student{
    var name:String
    var score:Int
    //这里使用lazy是因为下边用到了self.score 不使用lazy会提示我们self还没有初始化.
    //使用了懒加载就不一样了,等用到level的时候self肯定已经初始化了,就不会报错了。
    lazy var level :() -> String= {
        in
        switch self.score{
        case 0..<60:
            return "D"
        default:
            return "E"
        }
    }
    init (name:String, score:Int){
        self.name = name
        self.score = score
    }
    deinit {
        print("Student destoryed")
    }
}
var stu:Student?
stu = Student(name:"张三", score:45)
print(stu!.level())
stu = nil

控制台: 并没有输出销毁信息,证明循环引用了。

D
如何解决循环引用呢?

解决方法有三种

  • [weak self]
  • [unowned self]
  • weak var weakSelf = self 然后使用weakSelf
class Student{
    var name:String
    var score:Int
    //这里使用lazy是因为下边用到了self.score 不使用lazy会提示我们self还没有初始化.
    //使用了懒加载就不一样了,等用到level的时候self肯定已经初始化了,就不会报错了。
    lazy var level:() -> String = {
        //方式一:
        //[weak self] in
        //switch self!.score{
        
        //方式二:
        //[unowned self] in
        //switch self.score{
        
        //方式三:
        weak var weakSelf = self
        switch weakSelf!.score{
        case 0..<60:
            return "D"
        default:
            return "E"
        }
    }
    init (name:String, score:Int){
        self.name = name
        self.score = score
    }
    deinit {
        print("Student destoryed")
    }
}
var stu:Student?
stu = Student(name:"张三", score:45)
print(stu!.level())
stu = nil

控制台:

D
Student destoryed

证明Student对象销毁了,解决循环引用成功
这里详细解释下方式三
其实方式三是可以用的,只不过我这里的例子有点尴尬。
weak var weakSelf = self 这句代码是一定要放到闭包外边的,因为就是为了防止引用self。你放到里边之后还不是引用了。
我这里是因为在初始化完成之前不能使用self,会报错。所以方式三的道理是没错的。
有不懂的可以随时私信我。我会解答

相关文章

  • swift重温笔记(自动引用计数)

    自动引用计数的工作机制 自动引用计数实践 类实例之间的循环强引用 解决实例之间的循环强引用 闭包引起的循环强引用 ...

  • Swift--内存管理

    Swift内存管理概述 强引用循环 打破强引用循环 闭包中的强引用循环 Swift内存管理概述 Swift中的AR...

  • Block循环引用(五)

    1、什么是循环引用?循环引用会导致什么问题? 相互持有,双方都无法释放,导致循环引用,A强引用着B,B强引用着A ...

  • iOS底层 -- Blcok本质之循环引用

    一、产生循环引用的原因 由图可知,person对象强引用block,block强引用person对象,形成循环引用...

  • Swift Tour Learn (九) -- Swift 语法

    本章将会介绍 自动引用计数的工作机制自动引用计数实践类实例之间的循环强引用解决实例之间的循环强引用闭包引起的循环强...

  • iOS 内存泄漏之循环引用

    何为循环引用?简单理解:对象A强引用B,对象B也强引用A; 何时会发生循环引用?使用delegate、block的...

  • 笔记摘要(十一)--Swift3.0之解决实例/闭包的循环强引用

    解决实例之间的循环强引用可以通过定义类之间的关系为弱引用或无主引用,以替代强引用,从而解决循环强引用的问题。对于生...

  • IOS循环引用、内存泄漏、野指针

    一、循环引用和内存泄漏 1、block循环引用 分析:因为WGBlockTestViewController强引用...

  • 循环强引用

    循环强引用会导致内存泄漏内存泄漏(Memory Leak)是指程序中己动态分配的堆内存由于某种原因程序未释放或无法...

  • iOS基础 Block解决循环引用

    block 循环引用循环引用 A对象持有B,B又持有A,互相强引用 解决循环引用问题:ARC 和MRC ARC:...

网友评论

      本文标题:循环强引用

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