Swift内存分析

作者: dongwenbo | 来源:发表于2017-01-06 17:16 被阅读115次

    Swift使用ARC管理对象的的内存,ARC只适用于引用类型,引用计数会带来一些副作用,比如循环引用,所以Swift给我们提供了一些引用修饰来适应不同的情景,weakstrongunowned隐式可选
    引用计数为0销毁对象,不为0不销毁

    class A {
        deinit {
            print("析构器被调用")
        }
    }
    
    var a:A? = A()
    var b:A? = a
    var c:A? = b
    a = nil
    b = nil
    c = nil//销毁
    

    虽然Swift使用了ARC来帮助程序员管理内存,但是仍有一些特殊情况,需要程序员自己处理(添加各种标记帮助ARC工作)

    循环引用

    自己引用自己

    class A {
        var a:A?
        deinit {
            print("A析构器被调用")
        }
    }
    var a:A? = A()
    a?.a = a
    a = nil
    

    相互引用

    class A {
        var b:B?
        
        deinit {
            print("A析构器被调用")
        }
    }
    
    class B {
        var a:A?
        
        deinit {
            print("B析构器被调用")
        }
    }
    
    var a:A? = A()
    var b:B? = B()
    a?.b = b
    b?.a = a
    
    a = nil
    b = nil
    

    如何解决?

    弱引用和无主引用

    弱引用用weak标记,表示引用而不持有,引用计数不变,默认所有引用都是strong(可省略)

    class A {
        weak var a:A?//变成弱引用
        deinit {
            print("A析构器被调用")
        }
    }
    var a:A? = A()
    a?.a = a
    a = nil
    
    class A {
        var b:B?
        deinit {
            print("A析构器被调用")
        }
    }
    
    class B {
       weak var a:A?//将其中一个写成弱引用
        deinit {
            print("B析构器被调用")
        }
    }
    
    var a:A? = A()
    var b:B? = B()
    a?.b = b
    b?.a = a
    
    a = nil
    b = nil
    

    weak引用的位置决定了对象的释放顺序
    weak类型的引用在实例被销毁后自动置为nil(不会触发属性观察器), weak引用必须是 var可选,因为weak会在对象销毁时被置为nil

    无主引用
    无主引用和弱引用类似,都不会对实例进行强引用。区别是,无主引用的实例被销毁后并不会将无主引用置为nil。使用场景就是,必须保证无主引用始终指向一个未销毁的实例,无主引用必须是非可选的变量。使用关键字unowned标记无主引用

    class A {
        var b:B?
        deinit {
            print("A析构器被调用")
        }
    }
    
    class B {
        unowned let a:A
        init(a:A) {
            self.a = a
        }
        deinit {
            print("B析构器被调用")
        }
    }
    
    var block = {
        var a = A()
        var b = B(a: a)
        a.b = b
    }
    
    block()
    

    无主引用以及隐式解析可选属性

    两个实例都可以为nil,使用weak打破循环强引用
    两个实例其中一个可以为nil,另一个必须有值,使用unowned
    然而还存在第三种情况,两个都不能为nil,这种情况,需要一个类使用无主属性,而另外一个类使用隐式解析可选属性(隐式可选解析存在的意义😂)。

    class Country {
        let name: String
        var capitalCity: City!
        init(name: String, capitalName: String) {
            self.name = name//因为capitalCity是可选类型,所以此时已经初始化完毕,此后可以引用`self`
            self.capitalCity = City(name: capitalName, country: self)
        }
    }
    
    class City {
        let name: String
        unowned let country: Country
        init(name: String, country: Country) {
            self.name = name
            self.country = country
        }
    }
    

    这块理解的还不是很到位,就用官方的例子了

    闭包引起的循环强引用

    闭包是引用类型,一个类中有一个闭包属性,闭包属性中又使用强 self,就会造成循环强引用

    class A {
        var a:Int = 2
        var clousre = {
            
        }
        func abc() {
            weak var weakSelf = self;
            self.clousre = {
                weakSelf?.a = 4
            }
        }
        deinit {
            print("A析构器被调用")
        }
    }
    
    var block = {
        var a = A()
        a.abc()
        
    }
    block()
    

    可以将self转为weak,在闭包中使用类的成员必须使用self来访问,提醒程序员注意闭包的捕获特性,造成循环引用
    另外一种方法是使用捕获列表

    class A {
        var a:Int = 2
        var clousre = {
            
        }
        func abc() {
            self.clousre = {
                [weak self] in 
                self?.a = 4 //加了weak就变成可选的了,因为weak修饰的可能会被置为nil
            }
        }
        deinit {
            print("A析构器被调用")
        }
    }
    
    var block = {
        var a = A()
        a.abc()
        
    }
    block()
    

    在闭包参数前面定义[weak self],修饰符 和 引用名,其实还是转换成弱引用和无主引用,修饰符如何选择?还是根据上述三种场景
    1、两个实例相互引用,同时销毁,用unowned
    2、捕获的引用可以是nil时,用weak

    相关文章

      网友评论

        本文标题:Swift内存分析

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