自動引用計數
本書中以辦公室開關燈舉例:
假設辦公室的照明設備只有一個。上班進入辦公室的人需要照明,所以要把燈打開。
而對于下班離開的人來說,已經不需要照明了,所以要把燈關掉。但是如果該辦公室有很多人,該如何控制開關燈呢?
也就是最早來辦公室的人開燈,最后一個離開辦公室的人負責關燈。采用 引用計數說明的話就是:
- 辦公室沒人時引用計數為0 == 燈是關的
- 第一個人進入辦公室引用計數為1 == 開燈
- 第二個人進入辦公室引用計數為2 == 燈是開的
- 第三個人進入辦公室引用計數為3 == 燈是開的 以此類推
當下班時:
- 第一個人離開辦公室后引用計數為2 == 燈是開的
- 第二個人離開辦公室后引用計數為1 == 燈是開的
- 第三個人離開辦公室后引用計數為0 == 關燈
使用引用計數計算需要照明的人數,使辦公室的照明得到了很好的管理。同樣的,使用引用計數功能,對象也就能得到很好的管理,這就是Objective-C的內存管理。
內存管理的思考方式:
- 自己生成的對象,自己持有
- 非自己生成的對象,自己也能持有
- 不再需要自己持有的對象時釋放
- 非自己持有的對象無法釋放
對象操作 | Objective-C方法 |
---|---|
生成并持有對象 | alloc/new/copy/mutableCopy等方法 |
持有對象 | retain方法 |
釋放對象 | release方法 |
廢棄對象 | dealloc方法 |
這些有關Objective-C內存管理的方法,實際上不包括在該語言中,而是包含在Cocoa框架中用于OS X、iOS應用開發。Cocoa框架中Foundation框架類庫的NSObject淚擔負內存管理的職責。Objective-C內存管理中的alloc/retain/release/dealloc方法分別指代NSObject類的alloc類方法、retain實例方法、release實例方法和dealloc實例方法。
自己生成的對象自己持有
id obj = [[NSObject alloc]init]
id obj = [NSObject new]
alloc和new是完全一致的都是自己生成并持有對象,其中Copy方法利用NSCopying協議,它的實現方法copyWithZone:生成并持有對象的副本,mutableCopy方法利用NSMutableCopying協議,它的實現方法NSMutableCopyWithZone:生成并持有對象的副本
非自己生成的對象,自己也能持有
id obj = [NSMutableArray array]
取得對象的存在,但自己不持有對象
[obj retain]
自己持有對象
不再需要自己持有的對象時釋放
- 自己生成并持有的對象釋放
id obj = [[NSObject alloc]init]
自己生成并持有對象
obj release
釋放對象
- 非自己生成并持有的對象釋放
`id obj = [NSMutableArray array]`取得非自己生成并持有的對象
`[obj retain]`自己持有對象
`[obj release]`釋放對象
如果要用某個方法生成對象,并將其返回給該方法的調用方,又是怎么實現的呢?
```
-(id)allocObject {
id obj = [[NSObject alloc]init];
return obj;
}
```
調用該方法:
```
id obj1 = [obj0 allocObject];
```
那么,[NSMutableArray array]方法取得對象的存在,但自己不持有對象,又是如何實現的?
```
-(id)object {
id obj = [[NSObject alloc]init];// 自己持有對象
[obj autorelease];// 取得對象的存在,但自己不持有
return obj;// 返回對象
}
```
由此可以看出使用了autorelease方法將obj放入釋放池中由系統控制該對象的釋放,使對象在超出制定的生存范圍時能夠自動并且正確地釋放。
非自己持有的對象無法釋放
id obj = [NSMutableArray array];// 獲得非自己持有的對象
[obj release]; // 過度釋放crash
那么release和autorelease的區別在哪里?
當對象的引用計數大于0時release就將引用計數減一,當對象的引用計數等于0時,調用dealloc實例方法,廢棄對象。
}
autorelease實際上只是把對release的調用延遲了,對于每個autorelease,系統只是將對象放入了當前的autorelease pool中,當該pool被釋放時,該pool中的所有對象就會被release。而autorelease pool就是用來避免頻繁的申請/釋放內存。
- 釋放池的生命周期是與Runloop有關的,盡量避免在對象釋放后使用。
- 向上面提到的[NSMutableArray array]返回的對象自己并不持有,也就是說該對象已經放入釋放池中了,如果你想把它當成全局對象來使用,需要retain使引用計數+1,不需要時在release。
我們在創建一個程序時,有一個默認的autorelease pool,在程序退出時銷毀,那是不是所有的對象都放入了這個釋放池中,在程序退出時將其中的對象release呢?
并不是,這樣來說的話,與內存泄露并沒有區別了。其實,對于每個Runloop系統都會創建一個autorelease pool,并且這些釋放池棧式儲存,在每個Runloop結束的時候,棧頂的autorelease pool就會被銷毀,也就是說所有的對象都會被release。
未完
ARC[Automatic Reference Counting]規則