循环强引用

循环强引用会导致内存泄漏
内存泄漏(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,会报错。所以方式三的道理是没错的。
有不懂的可以随时私信我。我会解答

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,406评论 6 503
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,732评论 3 393
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,711评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,380评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,432评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,301评论 1 301
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,145评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,008评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,443评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,649评论 3 334
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,795评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,501评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,119评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,731评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,865评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,899评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,724评论 2 354

推荐阅读更多精彩内容