swift学习-自动引用计数(十六)

Swift 使用自动引用计数(ARC)机制来跟踪和管理你的应用程序的内存。通常情况下,Swift的内存管理机制会一直起着作用,

你无须自己来考虑内存的管理。ARC 会在类的实例不再被使用时,自动释放其占用的内存。

注意:

引用计数仅仅应用于类的实例。结构体和枚举类型是值类型,不是引用类型,也不是通过引用的方式存储和传递。

自动引用计数的工作机制

当创建一个新的实例的时候好,ARC会分配一块内存存储实例信息。当在实例不实用到时候,ARC释放所占的内存。为了确保实例在使用中不被销毁

ARC会跟踪和计算每一个实例正在被多少个属性、常量和变量引用,如果引用不为0则不会销毁。这种引用为强引用,因为它会将实例牢牢的保持住,只要强引用在,实例就不允许销毁。

class Person0 {

   let name: String

   init(name: String) {

       self.name = name

        print("\(name) is being initialized")

    }

   deinit {

        print("\(name) is being deinitialized")

    }

}

//声名三个变量为可选默认值为nil

var reference1:Person0?

var reference2:Person0?

var reference3:Person0?

reference1 = Person0(name:"John")

//John is being initialized 强引用为1

reference2 = reference1

//强引用为2

reference3 = reference1

//强引用为3


reference1 = nil

reference2 = nil

//强引用为1 没释放内存

reference3 = nil

//John is being deinitialized 强引用为0,释放内存


/*类实例之间的循环强引用

如果两个类实例互相持有对方的强引用,因而每个实例都让对方一直存在,就是这种情况。这就是所谓的循环强引用。

解决循环强引用:通过定义类之间的关系为弱引用或无主引用,以替代强引用,

*/


//居民

class Person {

    let name: String

    init(name: String) {

        self.name = name

    }

    var apartment: Apartment?

    deinit {

        print("\(name) is being deinitialized")

    }

}

//公寓

class Apartment {

    let uint: String

    init(uint: String) {

        self.uint = uint

    }

    //房客

    var tenant: Person?

    deinit {

        print("Apartment \(uint) is being deinitialized")

    }

}

var john: Person?

var uint4A: Apartment?

john = Person(name: "John Applessed")

//john引用Person实例

uint4A = Apartment(uint: "4A")

//uint4A引用Apartment实例


john?.apartment = uint4A

//Person.apartmentA引用Apartment实例

uint4A?.tenant = john

//Apartment.tenant引用Person实例

//产生循环强引用

john = nil

uint4A = nil 

//到这没有任何析构函数调用。当把这两个变量设为nil时,没有任何一个析构函数被调用。

//循环强引用会一直阻止PersonApartment类实例的销毁,这就在你的应用程序中造成了内存泄漏。

解决实例之间的循环强引用

Swift 提供了两种办法用来解决你在使用类的属性时所遇到的循环强引用问题:弱引用(weak reference)和无主引用

unowned reference)。

弱引用和无主引用允许循环引用中的一个实例引用另外一个实例而不保持强引用。这样实例能够互相引用而不产生循环强引用。


对于生命周期中会变为nil的实例使用弱引用。相反地,对于初始化赋值后再也不会被赋值为nil的实例,使用无主引用。

弱引用

弱引用不会对其引用的实例保持强引用,因而不会阻止 ARC销毁被引用的实例。这个特性阻止了引用变为循环强引用。

声明属性或者变量时,在前面加上weak关键字表明这是一个弱引用。

在实例的生命周期中,如果某些时候引用没有值,那么弱引用可以避免循环强引用。如果引用总是有值,则可以使用无主引用,

在无主引用中有描述。在上面Apartment的例子中,一个公寓的生命周期中,有时是没有居民的,因此适合使用弱引用来解决循环强引用。

注意:

弱引用必须被声明为变量,表明其值能在运行时被修改。弱引用不能被声明为常量。

//居民

class Person {

   let name: String

   init(name: String) {

       self.name = name

    }

   var apartment: Apartment?

   deinit {

        print("\(name) is being deinitialized")

    }

}

//公寓

class Apartment {

   let uint: String

   init(uint: String) {

       self.uint = uint

    }

    //房客弱引用

  weak var tenant:Person?

   deinit {

        print("Apartment\(uint) is being deinitialized")

    }

}

var john:Person?

var uint4A:Apartment?

john = Person(name:"John Applessed")

//john引用Person实例

uint4A = Apartment(uint:"4A")

//uint4A引用Apartment实例


john?.apartment = uint4A

//Person.apartmentA引用Apartment实例

uint4A?.tenant = john

//Apartment.tenant引用Person实例


john = nil

//John Applessed is being deinitialized 实例销毁

uint4A = nil

//Apartment 4A is being deinitialized 实例销毁


/*无主引用


和弱引用类似,无主引用不会牢牢保持住引用的实例。和弱引用不同的是,无主引用是永远有值的。

因此,无主引用总是被定义为非可选类型(non-optional type)。你可以在声明属性或者变量时,

在前面加上关键字unowned表示这是一个无主引用。


由于无主引用是非可选类型,你不需要在使用它的时候将它展开。无主引用总是可以被直接访问。不过 ARC

无法在实例被销毁后将无主引用设为nil,因为非可选类型的变量不允许被赋值为nil

*/

//客户

class Customer {

   let name: String

   var card: CreditCard?

   init(name: String) {

       self.name = name

    }

   deinit {

        print("\(name) is being deinitialized")

    }

}

//信用卡

class CreditCard {

   let number: uint64

//    信用卡总是关联着一个客户,定义为无主引用

   unowned let customer:Customer

   init(number: uint64, customer:Customer) {

       self.number = number

       self.customer = customer

    }

   deinit {

        print("Card #\(number) is being deinitialized")

    }

}

var jim: Customer?

jim =Customer(name: "jim san")

jim?.card =CreditCard(number: 1234_5689_0012_3265, customer: jim!)


jim =nil

//jim 被设置为nil后释放内存

//jim san is being deinitialized

//Card #1234568900123265 is being deinitialized


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

PersonApartment的例子展示了两个属性的值都允许为nil,并会潜在的产生循环强引用。这种场景最适合用弱引用来解决。

CustomerCreditCard的例子展示了一个属性的值允许为nil,而另一个属性的值不允许为nil,这也可能会产生循环强引用。

这种场景最适合通过无主引用来解决。

然而,存在着第三种场景,在这种场景中,两个属性都必须有值,并且初始化完成后永远不会为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:\(name) is deinitialized")

    }

}

//城市

class City {

   let name: String

   unowned let country:Country

   init(name: String, country:Country) {

       self.name = name

       self.country = country

    }

   deinit {

        print("City:\(name) is deinitialized")

    }

}

var country:Country? =Country(name: "中国", capitalName:"北京")

print("\(country!.name)的首都是\(country!.capitalCity.name)")

//中国的首都是北京

country = nil

//Country:中国 is deinitialized

//City:北京 is deinitialized


/*闭包引起的循环强引用

产生的原因:将一个闭包赋值给类实例的莫个属性,并且这个闭包又引用了类实例(闭包访问类属性,或方法导致闭包捕获类实例)。

*/


class HTMLElement {

   let name: String

   let text: String?

    //定义个参数为空返回类型为String(等价于()->String)的函数闭包

   lazy var asHTML:Void -> String = {

       if let text =self.text {

           return "<\(self.name)>\(text)\(self.name)>"

        }else{

           return "<\(self.name) />"

        }

    }

   init(name: String, text:String? = nil) {

       self.name = name

       self.text = text

    }

   deinit {

        print("\(name) is being deinitialized")

    }

}

var paragraph:HTMLElement? = HTMLElement(name:"p", text: "hello world")

print(paragraph!.asHTML())

//

hello world

paragraph = nil

//没有调用析构函数,应为asHTML闭包捕获了self


解决闭包引起的循环强引用

在定义闭包时同时定义捕获列表作为闭包的一部分,通过这种方式可以解决闭包和类实例之间的循环强引用。

捕获列表定义了闭包体内捕获一个或者多个引用类型的规则。跟解决两个类实例间的循环强引用一样,

声明每个捕获的引用为弱引用或无主引用,而不是强引用。应当根据代码关系来决定使用弱引用还是无主引用。


定义捕获列表

捕获列表中的每一项都由一对元素组成,一个元素是weakunowned关键字,另一个元素是类实例的引用(如self

或初始化过的变量(如delegate = self.delegate!)。这些项在方括号中用逗号分开。

如果闭包有参数列表和返回类型,把捕获列表放在它们前面:

lazy var someClosure: (Int, String) -> String = {

    [unowned self, weak delegate = self.delegate!](index: Int, stringToProgress:String) -> String in

    //closure body goes here

}

如果闭包没有指明参数列表或者返回类型,即它们会通过上下文推断,那么可以把捕获列表和关键字in放在闭包最开始的地方:


lazy var someClosure: Void -> String = {

[unowned self, weak delegate = self.delegate!] in

// closure body goes here

}

引用和无主引用

在闭包和捕获的实例总是互相引用时并且总是同时销毁时,将闭包内的捕获定义为无主引用。

相反的,在被捕获的引用可能会变为nil时,将闭包内的捕获定义为弱引用。弱引用总是可选类型,

并且当引用的实例被销毁后,弱引用的值会自动置为nil。这使我们可以在闭包体内检查它们是否存在。

注意:

如果被捕获的引用绝对不会变为nil,应该用无主引用,而不是弱引用。


class HTMLElement1 {

   let name: String

   let text: String?

    

   lazy var asHTML:Void -> String = {

        //定义捕获参数为无主引用

        [unownedself] in

       if let text =self.text {

           return "<\(self.name)>\(text)\(self.name)>"

        }else{

           return "<\(self.name) />"

        }

    }

   init(name: String, text:String? = nil) {

       self.name = name

       self.text = text

    }

   deinit {

        print("\(name) is deinitialized")

    }

}

var h1:HTMLElement1? =HTMLElement1(name: "p", text: "Hello, world")

print(h1!.asHTML())

//

Hello, world

h1 =nil

//p is deinitialized 调用析构器,实例销毁,释放内存

你可能感兴趣的:(swift)