swift(16)自动引用计数ARC
2014-07-03 16:46
387 查看
ARC:Automatic Reference Counting
ARC自动管理内存,原理就是类对象的指针被引用时计数加,被释放时计数减,如果为0则自动释放。
类的对象的内存一旦被ARC释放,则该对象不能再被使用,否则你的程序会崩溃或跑飞了
强引用:一个类的对象被赋值给一个变量,则这个变量对这个类对象的地址有强引用。
有强引用,则就有弱引用,被weak修饰的变量对赋值的类对象有弱引用。
先看一个例子来理解ARC是如何工作的,这时候析构函数派上用场了,析构的时候打印一句话,以让你知道它的实体被释放了
上面是最简单和最正常的情况,但是在实际应用中类之间往往有复杂的关系,可能会相互包含,相互之间产生强引用。比如下面的例子:住户和房子
住户john有房子,因此住户类有一个房子类型的变量number73
房子number73也有住户,因此房子类有一个住户类型的变量john
定义住户和房子,ARC计数都是1,然后相互赋值,ARC计数都是2。
此时释放john,他的ARC减1,但是房子number73的属性对john有强引用,所以john的内存没能被自动回收。
同样的道理,释number73,他的ARC减1,但是number73的内存被john强引用,也没有被回收内存,此时产生了内存泄露。
我觉得有一个方法,就是在释放john前,将number73的tenant属性置为nil。但是上例还是一个简单,关系很明确的例子,在实际应用中对john的引用可能不止一个类的一个对象,难道要挨个释放吗,这肯定是不可能的,所以引入了弱引用。
使用weak修饰的变量,对其赋值时,被赋值的对象ARC计数器不加。看修改后的例子:
上图的weak表示弱引用,我觉得可理解为ARC不加计数
还有一种解决上述问题的方式是无主引用,用法类似weak,关键字是unowned,我觉得是不拥有的意思吧,就是类实例虽然赋值给我了,但是我不拥有你的强引用,不对你的ARC有影响。
unowned变量不能声明为可选变量,也就是它不能被赋值为nil,并且他一旦存在时就得有值。
如果调用被释放了内存的unowned的变量,将发生运行时错误。
下面的例子是用户和银行卡的关系,银行卡肯定是有用户的,如果该用户被释放,则银行卡对象也被释放。
weak是两个类的属性都可以被设置为nil,unowned的是其中一个类的属性不能设置为nil,如果两个类的属性都不能设置为nil该怎么办呢,方法是一个使用无主引用,另一个使用隐式展开的可选属性(就是肯定有值,但是声明时初始值为nil,值是构造函数创建的)
关于闭包的强引用环,如果将一个闭包赋值给一个类的属性,并且这个闭包也是用这个类的实例,则产生闭包的强引用环,如下例:
解决这种强引用环使用占有列表
上述问题的解决办法:
ARC自动管理内存,原理就是类对象的指针被引用时计数加,被释放时计数减,如果为0则自动释放。
类的对象的内存一旦被ARC释放,则该对象不能再被使用,否则你的程序会崩溃或跑飞了
强引用:一个类的对象被赋值给一个变量,则这个变量对这个类对象的地址有强引用。
有强引用,则就有弱引用,被weak修饰的变量对赋值的类对象有弱引用。
先看一个例子来理解ARC是如何工作的,这时候析构函数派上用场了,析构的时候打印一句话,以让你知道它的实体被释放了
class Person { let name: String init(name: String) { self.name = name println("\(name) is being initialized") } deinit { println("\(name) is being deinitialized") } } //定义了4个可选变量,当变量被赋值为nil时,表示释放 var reference1: Person? var reference2: Person? var reference3: Person? //创建对象,ARC+1 reference1 = Person(name: "John Appleseed") // prints "John Appleseed is being initialized" //赋值,ARC+1+1 reference2 = reference1 reference3 = reference1 //释放连个ARC-1-1,释放两个后,还有一个reference3 对Person对象John Appleseed有强引用,所以不会释放 reference1 = nil reference2 = nil //释放reference3 后,ARC再-1,此时变为0,Person对象John Appleseed内存被释放归还操作系统 reference3 = nil // prints "John Appleseed is being deinitialized"
上面是最简单和最正常的情况,但是在实际应用中类之间往往有复杂的关系,可能会相互包含,相互之间产生强引用。比如下面的例子:住户和房子
住户john有房子,因此住户类有一个房子类型的变量number73
房子number73也有住户,因此房子类有一个住户类型的变量john
定义住户和房子,ARC计数都是1,然后相互赋值,ARC计数都是2。
此时释放john,他的ARC减1,但是房子number73的属性对john有强引用,所以john的内存没能被自动回收。
同样的道理,释number73,他的ARC减1,但是number73的内存被john强引用,也没有被回收内存,此时产生了内存泄露。
class Person { let name: String init(name: String) { self.name = name } var apartment: Apartment? deinit { println("\(name) is being deinitialized") } } class Apartment { let number: Int init(number: Int) { self.number = number } var tenant: Person? deinit { println("Apartment #\(number) is being deinitialized") } } var john: Person? var number73: Apartment? //看下图,两个竖着的箭头strong john = Person(name: "John Appleseed") number73 = Apartment(number: 73) //下图两个横着的箭头 john!.apartment = number73 number73!.tenant = john //去掉了两个竖着的箭头,但是横着的箭头还是存在的,所以无法自动释放内存 john = nil number73 = nil
我觉得有一个方法,就是在释放john前,将number73的tenant属性置为nil。但是上例还是一个简单,关系很明确的例子,在实际应用中对john的引用可能不止一个类的一个对象,难道要挨个释放吗,这肯定是不可能的,所以引入了弱引用。
使用weak修饰的变量,对其赋值时,被赋值的对象ARC计数器不加。看修改后的例子:
class Person { let name: String init(name: String) { self.name = name } var apartment: Apartment? deinit { println("\(name) is being deinitialized") } } class Apartment { let number: Int init(number: Int) { self.number = number } //就多了下面一个单词weak weak var tenant: Person? deinit { println("Apartment #\(number) is being deinitialized") } } var john: Person? var number73: Apartment? //john的ARC+1,number73的ARC+1 john = Person(name: "John Appleseed") number73 = Apartment(number: 73) number73的ARC = 2,john的ARC不变 john!.apartment = number73 number73!.tenant = john //john的ARC=0,被释放,同时其apartment成员被释放,因此number73的ARC-1 john = nil // prints "John Appleseed is being deinitialized" //number73的ARC再减1,变为0,内存也被回收 number73 = nil // prints "Apartment #73 is being deinitialized"
上图的weak表示弱引用,我觉得可理解为ARC不加计数
还有一种解决上述问题的方式是无主引用,用法类似weak,关键字是unowned,我觉得是不拥有的意思吧,就是类实例虽然赋值给我了,但是我不拥有你的强引用,不对你的ARC有影响。
unowned变量不能声明为可选变量,也就是它不能被赋值为nil,并且他一旦存在时就得有值。
如果调用被释放了内存的unowned的变量,将发生运行时错误。
下面的例子是用户和银行卡的关系,银行卡肯定是有用户的,如果该用户被释放,则银行卡对象也被释放。
class Customer { let name: String var card: CreditCard? init(name: String) { self.name = name } deinit { println("\(name) is being deinitialized") } } class CreditCard { let number: Int unowned let customer: Customer init(number: Int, customer: Customer) { self.number = number self.customer = customer } deinit { println("Card #\(number) is being deinitialized") } } var john: Customer? john = Customer(name: "John Appleseed") //银行卡被创建时,就有用户,银行卡的ARC是1,银行卡的customer对john的ARC没有影响,john的ARC仍然是1 john!.card = CreditCard(number: 1234_5678_9012_3456, customer: john!) //用户释放时,卡的ARC减1,也被释放了 john = nil // prints "John Appleseed is being deinitialized" // prints "Card #1234567890123456 is being deinitialized"
weak是两个类的属性都可以被设置为nil,unowned的是其中一个类的属性不能设置为nil,如果两个类的属性都不能设置为nil该怎么办呢,方法是一个使用无主引用,另一个使用隐式展开的可选属性(就是肯定有值,但是声明时初始值为nil,值是构造函数创建的)
class Country { let name: String //可选属性,!表示这个属性肯定是可用的,但是初始值是nil,目的是可以在构造函数中提前使用self let capitalCity: City! init(name: String, capitalName: String) { self.name = name //由于capitalCity 有初始值是nil,那么已经被赋值,类的所有属性都有初值,所以可以使用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 } } var country = Country(name: "Canada", capitalName: "Ottawa") println("\(country.name)'s capital city is called \(country.capitalCity.name)") // prints "Canada's capital city is called Ottawa"
关于闭包的强引用环,如果将一个闭包赋值给一个类的属性,并且这个闭包也是用这个类的实例,则产生闭包的强引用环,如下例:
class HTMLElement { let name: String let text: String? //lazy类型的变量肯定在类实例初始化完成后才可使用 @lazy var asHTML: () -> String = { //多次用到了自身实例self,所以产生了强引用,但是只有一次 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 { println("\(name) is being deinitialized") } } var paragraph: HTMLElement? = HTMLElement(name: "p", text: "hello, world") println(paragraph!.asHTML()) // prints "<p>hello, world</p>" paragraph = nil //没有释放
解决这种强引用环使用占有列表
@lazy var someClosure: (Int, String) -> String = { [unowned self] (index: Int, stringToProcess: String) -> String in // closure body goes here } //或者省略参数 @lazy var someClosure: () -> String = { [unowned self] in // closure body goes here } //都是表示不占用self
上述问题的解决办法:
class HTMLElement { let name: String let text: String? @lazy var asHTML: () -> String = { [unowned self] 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 { println("\(name) is being deinitialized") } } var paragraph: HTMLElement? = HTMLElement(name: "p", text: "hello, world") println(paragraph!.asHTML()) // prints "<p>hello, world</p>" paragraph = nil // prints "p is being deinitialized"
相关文章推荐
- Swift学习笔记16——自动引用计数(Automatic Reference Counting)
- Welcome-to-Swift-16自动引用计数(Automatic Reference Counting)
- swift:自动引用计数ARC
- IOS开发语言Swift入门连载---自动引用计数
- Swift:(十一)、构造器、自动引用计数、循环引用问题解决、类型转换
- Swift基础 自动引用计数和析构器
- swift 学习- 18 -- 自动引用计数
- OC语言学习09-自动引用计数ARC
- Swift3.0中文教程:16.自动引用计数
- Swift 自动引用计数(Automatic Reference Counting)
- 实例讲解Swift中引用类型的ARC自动引用计数
- 《iOS应用程序开发方法与实践》补充内容-2.14 自动引用计数ARC
- Swift的自动引用计数->解决内存泄露
- Swift自动引用计数 - 解决内存泄露
- Swift 自动引用计数(Automatic Reference Counting, ARC)
- Swift学习笔记系列——(15)自动引用计数
- Swift 自动引用计数(十四)
- 对照Java学习Swift--自动引用计数(Automatic Reference Counting)
- swift学习笔记之自动引用计数
- [翻译]Swift编程语言——自动引用计数