程序员iOS Developer

Swift内存分析

2017-01-06  本文已影响115人  dongwenbo

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

上一篇下一篇

猜你喜欢

热点阅读