聽說這本書很好荣暮,所以在項(xiàng)目不怎么忙的時(shí)候就讀了讀≌肿ぃ總結(jié)了點(diǎn)筆記穗酥。
手動(dòng)內(nèi)存管理MRC
- 內(nèi)存管理的思想
思想一:自己生成的對(duì)象,自己持有惠遏。
思想二:非自己生成的對(duì)象砾跃,自己也能持有。
思想三:不再需要自己持有的對(duì)象時(shí)釋放對(duì)象节吮。
思想四:非自己持有的對(duì)象無法釋放抽高。
從上面的思想來看,我們對(duì)對(duì)象的操作可以分為三種:生成透绩,持有翘骂,釋放,再加上廢棄帚豪,一共有四種碳竟。它們所對(duì)應(yīng)的Objective-C的方法和引用計(jì)數(shù)的變化是:
思想一:自己生成的對(duì)象,自己持有
alloc
new
copy
mutableCopy
如 id obj = [[NSObject alloc] init];//持有新生成的對(duì)象
思想二:非自己生成的對(duì)象狸臣,自己也能持有
id obj = [NSMutableArray array];//非自己生成并持有的對(duì)象
[obj retain];//持有新生成的對(duì)象
來看看[NSMutableArray array]是怎么個(gè)實(shí)現(xiàn)法莹桅,和自己生成的對(duì)象有什么區(qū)別:
- (id)object
{
id obj = [[NSObject alloc] init];//持有新生成的對(duì)象
[obj autorelease];//自動(dòng)釋放
return obj;
}
通過autorelease方法,使對(duì)象的持有權(quán)轉(zhuǎn)移給了自動(dòng)釋放池烛亦。所以實(shí)現(xiàn)了:調(diào)用方拿到了對(duì)象统翩,但這個(gè)對(duì)象還不被調(diào)用方所持有.
思想三:不再需要自己持有的對(duì)象時(shí)釋放對(duì)象
id obj = [[NSObject alloc] init];//持有新生成的對(duì)象
[obj release];//事情做完了,釋放該對(duì)象
或者是
id obj = [NSMutableArray array];//非自己生成并持有的對(duì)象
[obj retain];//持有新生成的對(duì)象
[obj release];//事情做完了此洲,釋放該對(duì)象
思想四:無法釋放非自己持有的對(duì)象
情況一:多次釋放厂汗。
id obj = [[NSObject alloc] init];//持有新生成的對(duì)象
[obj release];//釋放該對(duì)象,不再持有了
[obj release];//釋放已經(jīng)廢棄了的對(duì)象呜师,崩潰
情況二:對(duì)象不被自己持有娶桦,就釋放。
id obj = [NSMutableArray array];//非自己生成并持有的對(duì)象
[obj release];//釋放了非自己持有的對(duì)象
實(shí)現(xiàn)原理
Objective-C對(duì)象中保存著引用計(jì)數(shù)這一整數(shù)值汁汗。
調(diào)用alloc或者retain方法后衷畦,引用計(jì)數(shù)+1。
調(diào)用release后知牌,引用計(jì)數(shù)-1祈争。
引用計(jì)數(shù)為0時(shí),調(diào)用dealloc方法廢棄對(duì)象角寸。
autorelease
autorelease的具體使用方法如下:
生成并持有NSAutoreleasePool對(duì)象菩混。
調(diào)用已分配對(duì)象的autorelease方法忿墅。
廢棄NSAutoreleasePool對(duì)象。
所有調(diào)用過autorelease方法的對(duì)象沮峡,在廢棄NSAutoreleasePool對(duì)象時(shí)疚脐,都將調(diào)用release方法(引用計(jì)數(shù)-1)
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
id obj = [[NSObject alloc] init];
[obj autorelease];
[pool drain];//相當(dāng)于obj調(diào)用release方法
NSRunLoop在每次循環(huán)過程中,NSAutoreleasePool對(duì)象都會(huì)被生成或廢棄.
這樣會(huì)有個(gè)問題如果是生成大量的autorelease對(duì)象邢疙,只要不廢棄NSAutoreleasePool的話對(duì)象就不能釋放棍弄,所以會(huì)產(chǎn)生內(nèi)存不足的問題。由上圖NSRunLoop是在應(yīng)用程序主線程處理完了才會(huì)廢棄Pool疟游。所以如果在for循環(huán)里創(chuàng)建好多的局部對(duì)象呼畸,他們得不到及時(shí)的釋放,就會(huì)使得程序因?yàn)閮?nèi)存不足奔潰颁虐。
如
for (int i = 0; i < 10000; i++)
{
圖像文件讀入到data對(duì)象蛮原,
data生成UIimage對(duì)象,
改變對(duì)象的尺寸生成一個(gè)新的UIimage對(duì)象
}
解決辦法:
for (int i = 0; i < 10000; i++)
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
圖像文件讀入到data對(duì)象聪廉,
data生成UIimage對(duì)象,
改變對(duì)象的尺寸生成一個(gè)新的UIimage對(duì)象
[pool drain];//相當(dāng)于obj調(diào)用release方法
}
每個(gè)循環(huán)都會(huì)創(chuàng)建個(gè)池子故慈,池子也是循環(huán)完了就釋放板熊。所以自動(dòng)變量也都釋放了。自動(dòng)變量都是加在離它最近的池子的察绷。
蘋果的實(shí)現(xiàn)
class AutoreleasePoolPage
{
static inline void *push()
{
//生成或者持有 NSAutoreleasePool 類對(duì)象
}
static inline void pop(void *token)
{
//廢棄 NSAutoreleasePool 類對(duì)象
releaseAll();
}
static inline id autorelease(id obj)
{
//相當(dāng)于 NSAutoreleasePool 類的 addObject 類方法
AutoreleasePoolPage *page = 取得正在使用的 AutoreleasePoolPage 實(shí)例;
autoreleaesPoolPage->add(obj)
}
id *add(id obj)
{
//將對(duì)象追加到內(nèi)部數(shù)組中
}
void releaseAll()
{
//調(diào)用內(nèi)部數(shù)組中對(duì)象的 release 方法
}
};
//壓棧
void *objc_autoreleasePoolPush(void)
{
return AutoreleasePoolPage::push();
}
//出棧
void objc_autoreleasePoolPop(void *ctxt)
{
AutoreleasePoolPage::pop(ctxt);
}
id *objc_autorelease(id obj)
{
return AutoreleasePoolPage::autorelease(obj);
}
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
// 等同于 objc_autoreleasePoolPush
id obj = [[NSObject alloc] init];
[obj autorelease];
// 等同于 objc_autorelease(obj)
[pool drain];
// 等同于 objc_autoreleasePoolPop(pool)
想要了解更多autoreleasepool原理看這個(gè)http://blog.leichunfeng.com/blog/2015/05/31/objective-c-autorelease-pool-implementation-principle/
ARC下的內(nèi)存管理
在ARC機(jī)制下干签,編譯器就可以自動(dòng)進(jìn)行內(nèi)存管理,減少了開發(fā)的工作量拆撼。但我們有時(shí)仍需要四種所有權(quán)修飾符來配合ARC來進(jìn)行內(nèi)存管理容劳。
四種所有權(quán)修飾符
ARC提供四種修飾符,分別是strong, weak, autoreleasing, unsafe_unretained
__strong:強(qiáng)引用闸度,持有所指向?qū)ο蟮乃袡?quán)竭贩,無修飾符情況下的默認(rèn)值。如需強(qiáng)制釋放莺禁,可置nil留量。
NSObject *obj = [[NSObject alloc]init];
他們是等價(jià)的
NSObject *__strong obj = [[NSObject alloc]init];
obj = nil;
__weak:弱引用哟冬,不持有所指向?qū)ο蟮乃袡?quán)楼熄,引用指向的對(duì)象內(nèi)存被回收之后,引用本身會(huì)置nil浩峡,避免野指針可岂。
比如避免循環(huán)引用的弱引用聲明:
__weak __typeof(self) weakSelf = self;
_autoreleasing:將對(duì)象賦值給附有 _ autoreleasing 修飾符的變量等同于ARC 無效時(shí)調(diào)用對(duì)象的autorelease方法
@autoreleasepool {
id __autoreleasing obj = [[NSObject alloc] init];
}
unsafe_unretained:相當(dāng)于assign。直接賦值翰灾。引用計(jì)數(shù)不變缕粹。他會(huì)發(fā)生野指針現(xiàn)象稚茅。所以不安全。不像weak致开,當(dāng)指向的對(duì)象為空的時(shí)候峰锁,將指針置為nil。
屬性的內(nèi)存管理
ObjC2.0引入了@property双戳,提供成員變量訪問方法虹蒋、權(quán)限、環(huán)境飒货、內(nèi)存管理類型的聲明魄衅,下面主要說明ARC中屬性的內(nèi)存管理
屬性的參數(shù)分為三類,基本數(shù)據(jù)類型默認(rèn)為(atomic,readwrite,assign)塘辅,對(duì)象類型默認(rèn)為(atomic,readwrite,strong)晃虫,其中第三個(gè)參數(shù)就是該屬性的內(nèi)存管理方式修飾,修飾詞可以是以下之一:
assign:直接賦值
assign一般用來修飾基本數(shù)據(jù)類型
@property (nonatomic, assign) NSInteger count;
當(dāng)然也可以修飾ObjC對(duì)象扣墩,但是不推薦哲银,因?yàn)楸籥ssign修飾的對(duì)象釋放后,指針還是指向釋放前的內(nèi)存呻惕,在后續(xù)操作中可能會(huì)導(dǎo)致內(nèi)存問題引發(fā)崩潰荆责。
retain
retain和strong一樣,都用來修飾ObjC對(duì)象
使用set方法賦值時(shí)亚脆,實(shí)質(zhì)上是會(huì)先保留新值做院,再釋放舊值,再設(shè)置新值濒持,避免新舊值一樣時(shí)導(dǎo)致對(duì)象被釋放的的問題
MRC寫法如下
- (void)setCount:(NSObject *)count {
[count retain];
[_count release];
_count = count;
}
ARC對(duì)應(yīng)寫法
- (void)setCount:(NSObject *)count {
_count = count;
}
copy
一般用來修飾String键耕、Dict、Array等需要保護(hù)其封裝性的對(duì)象柑营,尤其是在其內(nèi)容可變的情況下屈雄,因此會(huì)拷貝(深拷貝)一份內(nèi)容給屬性使用,避免可能造成的對(duì)源內(nèi)容進(jìn)行改動(dòng)官套。
使用set方法賦值時(shí)棚亩,實(shí)質(zhì)上是會(huì)先拷貝新值,再釋放舊值虏杰,再設(shè)置新值讥蟆。
實(shí)際上,遵守NSCopying的對(duì)象都可以使用copy纺阔,當(dāng)然瘸彤,如果你確定是要共用同一份可變內(nèi)容,你也可以使用strong或retain笛钝。
weak:ARC新引入修飾詞质况,可代替assign愕宋,比assign多增加一個(gè)特性(置nil)
weak和strong一樣用來修飾ObjC對(duì)象。
使用set方法賦值時(shí)结榄,實(shí)質(zhì)上不保留新值中贝,也不釋放舊值,只設(shè)置新值臼朗。
@property (weak) id<MyDelegate> delegate;
strong:
ARC新引入修飾詞邻寿,可代替retain.ARC一般都寫strong。
block的內(nèi)存管理
OS中使用block必須自己管理內(nèi)存视哑,錯(cuò)誤的內(nèi)存管理將導(dǎo)致循環(huán)引用等內(nèi)存泄漏問題绣否,這里主要說明在ARC下block聲明和使用的時(shí)候需要注意的兩點(diǎn):
1)如果你使用@property去聲明一個(gè)block的時(shí)候,一般使用copy來進(jìn)行修飾(當(dāng)然也可以不寫挡毅,編譯器自動(dòng)進(jìn)行copy操作)蒜撮,盡量不要使用retain。
@property (nonatomic, copy) void(^block)(NSData * data);
block會(huì)對(duì)內(nèi)部使用的對(duì)象進(jìn)行強(qiáng)引用跪呈,因此在使用的時(shí)候應(yīng)該確定不會(huì)引起循環(huán)引用段磨,當(dāng)然保險(xiǎn)的做法就是添加弱引用標(biāo)記。
__weak typeof(self) weakSelf = self;
循環(huán)中對(duì)象占用內(nèi)存大
這個(gè)問題常見于循環(huán)次數(shù)較大耗绿,循環(huán)體生成的對(duì)象占用內(nèi)存較大的情景苹支。
for (int i = 0; i < 10000; i ++) {
Person * p = [[Person alloc]init];
}
該循環(huán)內(nèi)產(chǎn)生大量的臨時(shí)對(duì)象,直至循環(huán)結(jié)束才釋放缭乘,可能導(dǎo)致內(nèi)存泄漏沐序,在循環(huán)中創(chuàng)建自己的autoReleasePool琉用,及時(shí)釋放占用內(nèi)存大的臨時(shí)變量堕绩,減少內(nèi)存占用峰值。
for (int i = 0; i < 10000; i ++) {
@autoreleasepool {
Person * p = [[Person alloc]init];
}
}
特殊情況
假如有10000張圖片邑时,每張2M左右奴紧,現(xiàn)在需要獲取所有圖片的尺寸,你會(huì)怎么做晶丘?
用imageNamed方法加載圖片默認(rèn)會(huì)寫到緩存里黍氮,autoReleasePool也不能釋放緩存,對(duì)此問題需要另外的解決方法