美文网首页
循环引用问题

循环引用问题

作者: coderST | 来源:发表于2017-08-01 10:07 被阅读12次

    1 在自己写项目的时候一直没有发现一个大的BUG,因为这个bug是在首页,是整个程序都一直运行的界面,除非用户手动退出,这个类是我自己封装的,后来在用到另外的项目的时候(不在首页了),发现该界面一直不走deinit方法,让我有点饶头啊!!!

    • 经过努力的排场,发现界面循环引用了
       之前的定义
    // frame : 给定的尺寸
    
    // MARK:- 定义属性
        fileprivate var titles : [String]
        fileprivate var childsVC : [UIViewController]
        fileprivate var parentVC : UIViewController
        fileprivate var style : STPageViewStyle
        fileprivate var titleView : STTitlesView!
        fileprivate var titleViewParentView : Any?
    
        // titles : 标题的内容数组
        // childsVC : 对应标题控制器数组
        // parentVC : 父类控制器
        // style : 自定义数据
        // parentView : titleView被添加上的父类
        // titleViewParentView : 标题titleView被添加到哪里
    let pageView = STPageView(frame: rect, titles: titles, childsVC: childsVC, parentVC: self, style: style, titleViewParentView: nil)
    

    把从父类传进来的类,在定义属性前加weak进行弱引用

        fileprivate var titles : [String]
        fileprivate var childsVC : [UIViewController]
        `fileprivate weak var parentVC : UIViewController?`
        fileprivate var style : STPageViewStyle
        fileprivate var titleView : STTitlesView!
        fileprivate var titleViewParentView : Any?
    

    2 如果产生循环引用的两个属性一个允许为nil,另一个不允许为nil,这种情况适合用无主引用来解决

    • 只能在不能为nil的那个属性前面加unowned关键字,就是说 unowned设置以后即使它原来引用的内容已经被释放了,它仍然会保持对被已经释放了的对象的一个 "无效的" 引用,它不能是 Optional 值,也不会被指向 nil。如果尝试去调用这个引用的方法或者访问成员属性的话,程序就会崩溃.
      无主引用的例子:
    class Dog {
        let name: String
        var food: Food?
        init(name: String) {
            self.name = name
        }
        deinit { print("\(name)被销毁") }
    }
    class Food {
        let number: Int
        unowned var owner: Dog               // owner是一个无主引用
        init(number: Int, owner: Dog) {
            self.number = number
            self.owner = owner
        }
        deinit { print("食物被销毁") }
    }
    
    var dog1: Dog? = Dog(name: "Kate")
    dog1?.food = Food(number: 6, owner: dog1!) // dog强引用food,而food对dog是无主引用
    
    dog1 = nil                                 // 这样就可以同时销毁两个实例了
    
    • Dogfood属性可以为空,而Foodowner属性不能为空,我们把owner设为无主引用.

    3. 如果产生循环引用的两个属性都必须有值,不能为nil,这种情况适合一个类使用无主属性,另一个类使用隐式解析可选类型

    class Country {
        let name: String
        var capitalCity: City!                // 初始化完成后可以当非可选类型使用
        init(name: String, capitalName: String) {
            self.name = name
            self.capitalCity = City(name: capitalName, country: self)
        }
        deinit { print("Country实例被销毁") }
    }
    
    class City {
        let name: String
        unowned let country: Country
        init(name: String, country: Country) {
            self.name = name
            self.country = country
        }
        deinit { print("City实例被销毁") }
    }
    
    // 这样一条语句就能够创建两个实例
    var country: Country? = Country(name: "China", capitalName: "HangZhou")
    print(country!.name)                        // China
    print(country!.capitalCity.name)            // HangZhou
    country = nil                               // 同时销毁两个实例
    
    • Country的City属性后加!为隐式解析可选属性,类似可选类型,capitalCity属性的默认值为nil,一旦在Country的构造函数中给name属性赋完值后,Country的整个初始化过程就完成了,就能将self作为参数传递给City的构造函数了.
      总而言之,就是一条语句创建两个实例,还不产生循环引用.

    4 闭包也是引用类型,怎么解决闭包的循环强引用

    • 闭包中对任何其他元素的引用都是会被闭包自动持有的。如果我们在闭包中写了 self 这样的东西的话,那我们其实也就在闭包内持有了当前的对象。这里就出现了一个在实际开发中比较隐蔽的陷阱:如果当前的实例直接或者间接地对这个闭包又有引用的话,就形成了一个 self -> 闭包 -> self 的循环引用
    class Element {
        let name: String
        let text: String?
    
        lazy var group:() -> String = {        // 相当于一个没有参数返回string的函数
            [unowned self] in                   // 定义捕获列表,将self变为无主引用
            if let text = self.text {           // 解包
                return "\(self.name), \(text)"
            }else {
                return "\(self.name)"
            }
        }
    
        init(name: String, text: String? = nil) {
            self.name = name
            self.text = text
        }
        deinit { print("\(name)被销毁") }
    }
    
    var element1: Element? = Element(name: "Alex", text: "Hello")
    print(element1!.group())                     // Alex, Hello,闭包与实例相互引用
    
    element1 = nil                               // self为无主引用,实例能被销毁
    
    在闭包中定义一个捕获列表[unowned self],将self变为无主引用.这样就能够在避免产生循环强引用了.
    

    参考

    喵神的内存管理,WEAK 和 UNOWNED官方文档The Swift Programming Language的Language Guides部分的Automatic Reference Counting

    相关文章

      网友评论

          本文标题:循环引用问题

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