一.開篇之初
-
內(nèi)存管理解決的問題就是:
1)防止野指針的生成
(野指針:指向變量的指針還存在请垛,但是所指向的內(nèi)存已經(jīng)被釋放锨匆,此時的指針就變成了野指針 -- 沒有指向 “ 內(nèi)容 ” 的指針)2)防止出現(xiàn)內(nèi)存泄漏
(內(nèi)存泄漏:指向內(nèi)存空間的指針已經(jīng)被釋放封救,但是該指針指向的內(nèi)存空間還在內(nèi)存中存在(被占用) -- 沒有 “ 地址 ” 的內(nèi)存)3)合理使用內(nèi)存,防止有限內(nèi)存的大量消耗
Objective-C的內(nèi)存管理有三種圣勒,其中iOS中能用的膝迎,就是MRC(手動引用計數(shù))和ARC(自動引用計數(shù),官方推薦使用)灰蛙;而另外一個垃圾回收機制祟剔,只能用在OS X系統(tǒng)中。
內(nèi)存管理管理的范圍是摩梧,Objective-C 對象(基本數(shù)據(jù)類型由系統(tǒng)自動管理)物延。
MRC是基于引用計數(shù)的內(nèi)存管理,是否釋放內(nèi)存取決于引用計數(shù)是否為0仅父;但注意叛薯,真正要研究并不是引用計數(shù)浑吟,而是對象是否被持有的問題。
ARC是基于自動引用計數(shù)的內(nèi)存管理耗溜,是否釋放內(nèi)存取決于對象是否還有強引用指向组力;真正研究的是,對象的所有權(quán)問題抖拴。(所有權(quán)的概念是ARC中引入的)
二.內(nèi)存管理的思考方式
引自:《Objective-C高級編程 iOS與OS X多線程和內(nèi)存管理》
- 自己生成的對象燎字,自己所持有
- 非自己生成的對象,自己也能持有
- 自己持有的對象不再需要時釋放
- 非自己持有的對象無法釋放
換個方式來解讀:
- 自己申請的內(nèi)存阿宅,自己所掌管(擁有)
- 不是自己申請的內(nèi)存候衍,自己也可以掌管(擁有)
- 自己掌管(擁有)的內(nèi)存不再需要時就釋放(free)
- 不是自己掌管(擁有)的內(nèi)存,無法釋放(free)
三.MRC(Manual Reference Counting)內(nèi)存管理
--> 小小結(jié) <--
MRC模式下對象什么時候被銷毀洒放?引用計數(shù)值為0的時候蛉鹿。
-
MRC使用的管理內(nèi)存的基本方法和屬性:
- 四個方法 --> retain/release/dealloc/autorelease/
- 一個屬性 --> retainCount(記錄引用計數(shù)值)
<strong><----均定義在 NSObject.h 中----></strong>
//define OBJC_ARC_UNAVAILABLE __attribute__((unavailable("not available in automatic reference counting mode")))
- (instancetype)retain OBJC_ARC_UNAVAILABLE;
- (oneway void)release OBJC_ARC_UNAVAILABLE;
- (instancetype)autorelease OBJC_ARC_UNAVAILABLE;
- (NSUInteger)retainCount OBJC_ARC_UNAVAILABLE;
- (struct _NSZone *)zone OBJC_ARC_UNAVAILABLE;
- 誰retain,誰release
- retain既是把retainCount值加 1往湿; release既是把retainCount值減 1
- dealloc只有在 retainCount = 0 的時候妖异,由系統(tǒng)自動調(diào)用
- autorelease是把對象加進自動釋放池中,由系統(tǒng)自動為池中的對象發(fā)送release消息
問題 1:什么是引用計數(shù)(Reference Counting)煌茴?
引用計數(shù) ?
這里的“計數(shù)”表明必然會有一個東西(變量)來記錄引用的變化随闺,而在OC里這個變量就是retainCount;那么還有一個問題就是通過什么方式來操作這個變量蔓腐,OC里就是retain(引用次數(shù)加 1)矩乐,release(引用計數(shù)減 1 )方法。
- 引用計數(shù):就是分配的<strong> 內(nèi)存區(qū)塊 </strong>被<strong> 多少個 </strong>OC對象所持有(掌管回论;保持且擁有)散罕,間接表示就是retainCount值的大小。
注:對象傀蓉,指人可以識別的東西欧漱,具備屬性、收發(fā)信息葬燎、處理信息误甚;而從系統(tǒng)的角度看,操作對象就是操作一塊內(nèi)存谱净。(可能不是很準(zhǔn)確......)
問題 2 :引用計數(shù)如何管理OC對象窑邦?
首先明確,引用計數(shù)的變化是被持有者的變化壕探。
那么問題就是怎樣持有對象(持有內(nèi)存)?
持有:就是可以訪問內(nèi)存冈钦,且可以進行讀寫操作,而一般是通過內(nèi)存的首地址進行內(nèi)存的訪問李请,就是指針訪問瞧筛。
而OC中一般用來分配內(nèi)存的的函數(shù)是
alloc/new/copy/mutablecopy
(當(dāng)然還有clloc...
等等)厉熟,它們返回的都是指針,就是使用他們來生成對象并持有對象的较幌。問題 3:持有揍瑟?釋放?銷毀乍炉?對象... , 請看下表:
OC操作方法 | 對象的操作 | retainCount |
---|---|---|
alloc/new/copy/mutablecopy等 | 生成并持有對象 | 1月培? |
retain | 持有對象 | +1 |
release | 釋放對象 | -1 |
dealloc | 銷毀對象 | 此時該值沒有意義 |
autorelease | 在自動釋放池結(jié)束時,為里面的對象發(fā)送一條release消息 | (all object) -1 |
上面涉及的方法定義如下(NSOject.h):
//define OBJC_SWIFT_UNAVAILABLE(_msg) __attribute__((availability(swift, unavailable, message=_msg)))
+ (instancetype)new OBJC_SWIFT_UNAVAILABLE("use object initializers instead");
+ (instancetype)alloc OBJC_SWIFT_UNAVAILABLE("use object initializers instead");
- (void)dealloc OBJC_SWIFT_UNAVAILABLE("use 'deinit' to define a de-initializer");
- (id)copy;
- (id)mutableCopy;
問題 4:什么是自動釋放池恩急?
自動釋放池:在自動釋放池結(jié)束時,系統(tǒng)自動為里面的對象發(fā)送一條release消息(when the pool itself is drained)
要使用自動釋放池就要使用NSAutoreleasePool對象
- NSAutoreleasePool它的方法
使用方法:
創(chuàng)建一個NSAutoreleasePool對象
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
添加要釋放的對象進NSAutoreleasePool對象中
id obj = [NSString alloc] initWithstring:@"objective-c pool"];
[obj autorelease];
或[pool addObject:obj];
--- 1釋放NSAutoreleasePool對象
[pool drain];
等同于[pool release];
--- 2
注意:
1 --> 建議使用autorelease方法纪蜒,因為后面的方法會導(dǎo)致同一個對象被多次加入自動釋放池中衷恭。
2 --> 雖然兩個方法效果等同,但還是建議使用自動釋放池專門的drain方法纯续。
補充autorelease方法
問題 5:MRC下如何防止野指針訪問?
野指針訪問:指向的內(nèi)存空間已經(jīng)被釋放了随珠,但是指針還指向著已經(jīng)被釋放的內(nèi)存,此時的指針就是野指針猬错。
訪問了不存在的內(nèi)存窗看,當(dāng)然會引起程序崩潰
修改Xcode工程為MRC模式
-
開啟對應(yīng)targets的僵尸對象檢測,詳細(xì)步驟如下:
情況 1:過快釋放了對象(不要理retaiinCount把注意力放在對象被持有的個數(shù)上)
-
retainCount的補充:
-
程序代碼和運行結(jié)果
問題 6:MRC下如何防止內(nèi)存泄漏显沈?
自己生成的對象,自己所持有
非自己生成的對象逢唤,自己也能持有
自己持有的對象不再需要時釋放
非自己持有的對象無法釋放
補充:
疑問:mArrayCopy的retainCount是2 ?被持有者有兩個拉讯?
從這里就可以證明了,cope出來的新對象只是被mArrayCopy自己所持有而已鳖藕,所以當(dāng)release一次的時候?qū)ο笠呀?jīng)被釋放了魔慷,如果再release就是野指針訪問了(注:直接看持有者有多少)。
代碼:
/**
* alloc就是分配內(nèi)存的意思著恩,返回了一個指向內(nèi)存首地址的指針
*/
NSMutableArray *mArrayAlloc = [[NSMutableArray alloc] init]; // mArrayAlloc 持有對象
/**
* new 就相當(dāng)于alloc+init,但是new有可能會返回同一個對象院尔,所以并不建議使用
*/
NSMutableArray *mArrayNew = [NSMutableArray new]; // mArrayNew 持有對象
/**
* copy是一個實例方法,具體如下:
* - (id)copy
* Returns the object returned by copyWithZone:.
* - (id)copyWithZone:(NSZone *)zone
* Returns a new instance that’s a copy of the receiver.
* ---new instance 就表明了創(chuàng)建了一個新的內(nèi)存喉誊,并返回首地址(id 相當(dāng)于 void *)
*/
NSMutableArray *mArrayCopy = [mArrayAlloc copy]; //mArrayCopy 持有了對象
/**
* Returns the object returned by mutableCopyWithZone:.
* - (id)mutableCopy
* - (id)mutableCopyWithZone:(NSZone *)zone
* Returns a new instance that’s a mutable copy of the receiver.
* ---new instance 就表明了創(chuàng)建了一個新的內(nèi)存邀摆,并返回首地址(id 相當(dāng)于 void *)
*/
NSMutableArray *mArrayMutablecopy = [mArrayNew mutableCopy];//mArrayMutablecopy 持有了對象
- 持有對象
- 使用copy來獨立管理內(nèi)存
如果內(nèi)存還在使用的話,當(dāng)然不要把對象賦值為nil
對象之間相互持有的情況
-
程序代碼
如果要達到目的裹驰,apple讓girl也持有隧熙,就要在girl得到apple的時候持有一下,而可以做持有操作的是retain幻林,來看看:
我們知道對象在最后銷毀的時候是調(diào)用了dealloc方法的贞盯,那么girl既然持有了apple那么在銷毀自己的時候是不是應(yīng)該把自己持有的東西給交出來(釋放掉)音念,已死的對象不可能持有東西了吧,所以在girl的dealloc方法中加上apple釋放的代碼:
雖然上面的方法是可以的躏敢,但是有問題闷愤,問題如下:
apple再持有一下[[Apple alloc] init],再給girl件余,直接翻譯都是問題讥脐,而且從封裝性來看,girl要持有apple應(yīng)該是自己去持有啼器,也就是要自己進行retain烂瘫,而不是要apple先retain再給girl,
代碼優(yōu)化:
還有立磁,如果我們從現(xiàn)實生活中考慮問題(面向?qū)ο笫乾F(xiàn)實世界的抽象)韩玩,girl會不會只要一次apple呢?多要幾個~~
為了防止內(nèi)存泄漏损谦,我得這么干岖免,估計你看到這就想呵呵了:
再次優(yōu)化代碼,目的是只要girl再次要一個新的apple就給它持有照捡,如果是拿原來的apple當(dāng)然不再次持有咯:
代碼修改成下面這樣就是真正的颅湘,girl直接的持有一個新的apple(新的內(nèi)存空間)了,不過結(jié)果是一樣的栗精,正常釋放:
ARC( Automatic Reference Counting)內(nèi)存管理
- 請期待下一篇......