iOS内存管理—MRC
文章目錄
- 預備知識
- 手動引用計數MRC
- 自己生成的對象,自己持有
- 非自己生成的對象,自己也能持有
- 不再需要自己持有的對象時釋放
- 無法釋放非自己持有的對象
- @property參數
- MRC中避免循環retain
- retainCount
- autorelease
預備知識
- 繼承了NSObject的對象存儲在操作系統的堆里,堆一般由程序員分配釋放,程序結束時可能有OS回收。
- 非OC對象一般放在操作系統的棧里,棧由操作系統自動分配釋放,存放函數的參數值,局部變量的值等。其操作方式類似于數據結構中的棧(先進后出)
五大內存區域:棧區、堆區、全局區、常量區、代碼區
- 自動垃圾收集
- 手動引用計數 MRC
- 自動引用計數 ARC
在項目中打開MRC
手動引用計數MRC
- 引用計數器,可以理解為對象被引用的次數,每一個對象都有自己的引用計數器,引用計數為0時,對象占用的內存會被回收
內存管理的思考方式
- 自己生成的對象,自己持有
- 非自己生成的對象,自己也能持有
- 不再需要自己持有的對象時釋放
- 非自己持有的對象無法釋放
自己生成的對象,自己持有
使用alloc、new、copy、mutableCopy開頭的方法意味著自己生成的對象只有自己持有,不需要retain
//自己生成并持有對象 id obj1 = [[NSObject alloc] init]; id obj2 = [[NSObject new];非自己生成的對象,自己也能持有
用 alloc / new / copy / mutableCopy 以外的方法取得的對象,因為非自己生成并持有,所以自己不是該對象的持有者。
例如NSMutableArray類的array類方法。
[NSMutableArray array] 相當于 [[[NSMutableArray alloc] init] autorelease];
使用autorelease方法,可以使取得的對象存在,但自己不持有對象。通過retain操作持有對象。
不再需要自己持有的對象時釋放
id obj = [[NSObject alloc] init];//釋放對象 [obj release];無法釋放非自己持有的對象
//這一部分存在疑惑 id obj = [[NSObject alloc] init]; [obj release]; [obj release]; NSLog(@"%p", obj);- 這段代碼在執行NSLog時可能會報錯,因為對象所占的內存在“解除分配”之后,只是放回了“可用內存池”。如果執行NSLog時尚未覆寫對象內存,那么該對象仍然有效,這時程序不會崩潰,所以調試過程中可能結果不一樣,為避免在不經意間使用了無效對象,一般調完release之后都會清空指針。這就能保證不會出現可能指向無效對象的指針,這種指針通常稱為“懸掛指針”。
-
這部分存在疑惑的是,對obj進行第二次release操作時,應該會導致崩潰,但是并沒有…
-
(補充)對于對象的內存回收的理解:當對象的被回收時,其所占有的內存空間就可以分配給別人,在沒有分配給別人之前,對象的數據還存在。
@property參數
- 在成員變量前加上@property,系統就會自動幫我們生成基本的setter/getter方法
- 如果在property后邊加上retain,系統就會自動幫我們生成getter/setter方法內存管理的代碼,但是仍需要我們自己重寫dealloc方法
- 如果在property后邊加上assign,系統就不會幫我們生成set方法內存管理的代碼,僅僅只會生成普通的getter/setter方法,默認什么都不寫就是assign
MRC中避免循環retain
//Person.h #import <Foundation/Foundation.h> @class Son;NS_ASSUME_NONNULL_BEGIN@interface Person : NSObject@property (nonatomic, retain) Son *son;@end//Son.h #import <Foundation/Foundation.h> @class Person; NS_ASSUME_NONNULL_BEGIN@interface Son : NSObject@property (nonatomic, retain) Person *p;@end //main.m Son *son = [[Son alloc] init]; Person *person = [[Person alloc] init];son.p = person; person.son = son;[person release]; [son release]; [son release];會造成內存泄漏,堆中的兩個對象的內存回收不了
解決辦法:
- 一端用retain,一端用assign
retainCount
NSObject協議中定義了下列方法,用于查詢對象當前的引用計數
- (NSUInteger)retainCount;
ARC中已將此方法廢棄
保留計數的絕對數值一般都與開發者所應留意的事情完全無關,即便在調試時會用到此方法,通常也還是無所助益的。
此方法之所以無用,首要原因在于:他所返回的保留計數只是某個給定時間點上的值。并未考慮到對象是否處于自動釋放池中稍后系統會將自動釋放池情況這種情況。
舉個例子
有兩個錯誤
- 并未考慮到后續自動釋放操作,只是不停通過釋放操作降低保留計數,直至保留計數為0被系統回收,并未考慮到對象是否在自動釋放池中。如果對象在自動釋放池中,在系統清空池子時,會對對象再做一次release操作,這時就會導致程序崩潰。
- retainCount可能永遠不會返回0,因為有時系統會優化對象的釋放行為,在保留計數還是1的時候就把它回收了。只有在系統不打算這么優化時,計數值才會遞減至0。所以,這段代碼可以正常運行,多半是運氣。
在我們希望系統回收對象時,應確保沒有尚未抵消的retain保留操作,避免內存泄漏。
保留計數值過大原因
打印結果:
前三個對象皆為“單例對象”,所以其保留計數都很大。
- 系統會盡可能把NSString實現成單例對象。如果字符串像例子上的那樣,是一個編譯期常量,那么就可以這樣來實現了。在這種情況下,編譯器會把NSString對象所表示的數據放在應用程序的二進制文件里,這樣的話,運行程序時就可以直接用了,無需再創建NSString對象。
- NSNumber也類似,它使用了一種叫做“標簽指針”的概念來標注指定類型的數值。這種做法不使用NSNumber對象,而是把與數值有關的全部消息都放在指針值里。運行期系統會在消息派發期間檢測到這種標簽指針,并對它執行相應操作,使其行為看上去和真正的NSNumber對象一樣。這種優化只在某些場合使用,例子中的浮點數對象就沒有優化,所以保留計數為1
對于上述所說的單例對象,其保留計數絕對不會改變。這種對象的保留及釋放操作都是“空操作”。
打印結果:
2.0 36條中明確指出:不要使用retainCount
我們在MRC中,有時可能會想要打印引用計數,但retainCount方法并不是很有用,由于對象可能會處于自動釋放池中,這會導致打印的引用計數并不精準,而且其他程序庫也很有可能自行保留或釋放對象,這都會擾亂引用計數的具體值。
autorelease
顧名思義,autorelease就是自動釋放。
類似于C語言的局部變量,超出變量作用域,局部變量就會被廢棄,不可再訪問。
autorelease會在超出作用域時,調用對象的release實例方法,與C語言不同的是,編程人員可以設定變量的作用域
autorelease方法會返回對象本身,且調用完autorelease方法后,對象的計數器不變。autorelease實際上只是把對release的調用延遲了,對于每一個autorelease,系統只是把該對象放入了當前的autorelease pool中,當該pool被釋放時,該pool中的所有對象會被調用release。
autorelease的具體使用方法如下:
- 生成并持有NSAutoreleasePool對象
- 調用已分配對象的autorelease實例方法
- 廢棄NSAutoreleasePool對象
對于所有調用過autorelease實例方法的對象,在廢棄NSAutoreleasePool對象時,都將調用release實例方法。
內容不完整,后續補充
總結
以上是生活随笔為你收集整理的iOS内存管理—MRC的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 湿度控制c语言程序,基于51单片机的湿度
- 下一篇: 7-6 数据结构实验之链表七:单链表中重