Objective-C中的內(nèi)存管理

本文來自內(nèi)存管理文檔的整理

在Objective-C中內(nèi)存管理是基于引用計(jì)數(shù)的,所謂的引用計(jì)數(shù)就是每個(gè)對象都會有一個(gè)引用計(jì)數(shù)記錄這個(gè)對象跟誰有聯(lián)系,當(dāng)這個(gè)引用計(jì)數(shù)為0的時(shí)候,對象就會釋放.

在Cocoa框架下內(nèi)存管理的策略:

1.你自己通過new alloc,copy,mutableCopy創(chuàng)建的對象,則自己擁有這個(gè)對象的所有權(quán).

2.你可以使用retain獲取對象的所有權(quán),即對其引用計(jì)數(shù)+1.通過這個(gè)方法延長對象的聲明周期,防止其提前釋放掉

3.當(dāng)你不需要這個(gè)對象的時(shí)候,記得使用release或autorelease放棄你對對象的所有權(quán)

4.你不能釋放不是你擁有的對象的所有權(quán).

ps: 使用非 new ,alloc,copy,mutableCopy創(chuàng)建的對象,如[NSString stringWithFormat:@""]則得到的對象會自動的被標(biāo)記為autorelease.

MRR下實(shí)現(xiàn)set get 方法:

//set 方法

- (void)setCount:(NSNumber *)newCount {

[newCount retain];

[_count release];

// Make the new assignment.

_count = newCount;

}

//get方法

- (NSNumber *)count {

return _count;

}

5.weak引用是一種非擁有的狀態(tài),使用weak修飾的變量并不會retain weak指向的對象.日常開發(fā)中有一下幾種情況需要特殊注意:

delegate,Block,還有Timer. delegate和Block老生常談這就細(xì)說了.

在NSTimer的文檔中我們可以看到關(guān)于target的注解:

The object to which to send the message specified by?aSelector?when the timer fires. The timer maintains a strong reference to?target?until it (the timer) is invalidated.

timer 會對target保持強(qiáng)引用直到timer調(diào)用invalidated方法.若是target也持有timer那么循環(huán)引用就會產(chǎn)生了

而且因?yàn)閠imer是一個(gè)循環(huán)的調(diào)用,會被加入到Runloop中,如果不調(diào)用invalidated,則timer會被Runloop一直強(qiáng)引用,直到Runloop退出.若是主線程的Runloop則會一直等到程序結(jié)束,timer都不會被釋放

平常開發(fā)中記得一定要調(diào)用invalidated方法一般不會出現(xiàn)問題,但是某些特定的情況還是會出現(xiàn)沒有合適的時(shí)機(jī)去調(diào)用invalidated,所以要避免循環(huán)引用.

目前有幾種比較合理的解決方案:

第一中,寫一個(gè)Timer的分類,讓timer的target引用timer的類指針,這樣避免產(chǎn)生循環(huán)引用

typedefvoid(^QSExecuteTimerBlock) (NSTimer*timer);

@interfaceNSTimer(QSTool)

+ (NSTimer*)qs_scheduledTimerWithTimeInterval:(NSTimeInterval)timeInterval executeBlock:(QSExecuteTimerBlock)block repeats:(BOOL)repeats;

@end

@implementationNSTimer(QSTool)

+ (NSTimer*)qs_scheduledTimerWithTimeInterval:(NSTimeInterval)timeInterval executeBlock:(QSExecuteTimerBlock)block repeats:(BOOL)repeats{

NSTimer*timer = [selfscheduledTimerWithTimeInterval:timeInterval target:selfselector:@selector(qs_executeTimer:) userInfo:[blockcopy] repeats:repeats];

return timer;

}+ (void)qs_executeTimer:(NSTimer*)timer{?

?QSExecuteTimerBlock block = timer.userInfo;

if(block) { block(timer); }

}

@end

這個(gè)方案比較簡潔,好用比較推薦使用,另外這段代碼要感謝@南華coder.

第二種方法就是通過一個(gè)代理,讓代理去弱引用真的target,而timer去持有代理,這樣就避免了循環(huán)引用了.下面是代理的實(shí)現(xiàn):

@implementation BTWeakTimerTarget

{

?__weak target;?

?SEL selector;

}

[...]

- (void)timerDidFire:(NSTimer *)timer{?

?if(target) {

?[target performSelector:selector withObject:timer];?

?} else {

?[timer invalidate];?

?}}

@end

然后,你使用的時(shí)候

BTWeakTimerTarget *target = [[BTWeakTimerTarget alloc] initWithTarget:self selector:@selector(tick)];

timer = [NSTimer scheduledTimerWithTimeInterval:30.0 target:target selector:@selector(timerDidFire:) ...];

ps:在Cocoa框架內(nèi)Notification 和 KVO 都是weak 引用

NSNotificationCenter 會若引用被監(jiān)聽的對象

Neither the object receiving this message, nor?observer, are retained

這段話是KVO的官方文檔的解釋

KVO 中無論是觀察者還是被觀察者,兩者都是若引用

6.不要在dealloc內(nèi)部做一些有關(guān)資源的操作

7.集合類型當(dāng)你把一個(gè)對象加入其中,會被自動retain,當(dāng)你把對象從集合中移除則會被自動realease

8.不要依賴引用計(jì)數(shù)去判斷內(nèi)存是否泄漏這些問題,因?yàn)橐糜?jì)數(shù)并不準(zhǔn)確,有可能有錯(cuò)誤.

接下來就會介紹比較復(fù)雜的Autorelease

在NSObjec.mm文件可以看到Autorelease的實(shí)現(xiàn),代碼太多不放上來,我們看一下注釋

/*

Autorelease pool implementation

? A thread's autorelease pool is a stack of pointers.

? Each pointer is either an object to release, or POOL_BOUNDARY which is

? ? an autorelease pool boundary.

? A pool token is a pointer to the POOL_BOUNDARY for that pool. When

? ? the pool is popped, every object hotter than the sentinel is released.

? The stack is divided into a doubly-linked list of pages. Pages are added

? ? and deleted as necessary.

? Thread-local storage points to the hot page, where newly autoreleased

? ? objects are stored.

*/

簡單翻譯一下就是:

每個(gè)線程的?autoreleasepool 是一個(gè)儲存指針的棧

每個(gè)指針都代表一個(gè)要釋放的對象或者是?autoreleasepool的邊界

一個(gè) pool token 是一個(gè)指向POOL_BOUNDARY的指針,當(dāng)pool開始pop的時(shí)候,每個(gè)哨兵對象后面的對象都會釋放

這個(gè)棧是一個(gè)以page為節(jié)點(diǎn)的雙向鏈表

Thread-local storage(線程局部存儲)指向 hot page 怎抛,即最新添加的 autoreleased 對象所在的那個(gè) page 。

基本可以得到一些信息,Autorelease pool 其實(shí)就是一個(gè)棧,內(nèi)部存儲自動釋放的對象,當(dāng)達(dá)到一個(gè)臨界點(diǎn)的時(shí)候就會釋放.這個(gè)臨界點(diǎn)我們可以在官方文檔中看到

At the end of the autorelease pool block, objects that received an?autoreleasemessage within the block are sent a?release?message—an object receives a?release?message for each time it was sent an?autorelease?message within the block.

意思就是當(dāng)自動釋放的對象在超出自動釋放池的作用域后開始準(zhǔn)備釋放

那么什么時(shí)候需要自己創(chuàng)建自動釋放池呢

1.當(dāng)你寫一個(gè)不是基于UI Framework的程序,比如命令行工具

2.當(dāng)你寫一個(gè)循環(huán)創(chuàng)建了大量的臨時(shí)變量

3.當(dāng)你創(chuàng)建了子線程的時(shí)候

寫到這,可能大家會說平常開發(fā),出現(xiàn)子線程并沒有創(chuàng)建Autorelease Pool,也沒有內(nèi)存泄漏.

這個(gè)問題我也是很疑惑,不過看了官方文檔的描述和@Joy___的文章之后,大概知道了原因了,

官方文檔中有描述到,每個(gè)線程都會有自己的Autorelease Pool棧,在子線程你創(chuàng)建了 Pool 的話楞遏,產(chǎn)生的 Autorelease 對象就會交給 pool 去管理省店。如果你沒有創(chuàng)建 Pool 包晰,但是產(chǎn)生了 Autorelease 對象趟畏,就會調(diào)用 autoreleaseNoPage 方法。在這個(gè)方法中闰靴,會自動幫你創(chuàng)建一個(gè) hotpage(hotPage 可以理解為當(dāng)前正在使用的 AutoreleasePoolPage牡肉,如果你還是不理解捧灰,可以先看看 Autoreleasepool 的源代碼,再來看這個(gè)問題 )统锤,并調(diào)用page->add(obj)將對象添加到 AutoreleasePoolPage 的棧中毛俏,也就是說你不進(jìn)行手動的內(nèi)存管理,也不會內(nèi)存泄漏啦饲窿!StackOverFlow 的作者也說道煌寇,這個(gè)是 OS X 10.9+和 iOS 7+ 才加入的特性。并且蘋果沒有對應(yīng)的官方文檔闡述此事逾雄,但是你可以通過源碼了解阀溶。


以上就是對Object-c的內(nèi)存理解

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末腻脏,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子银锻,更是在濱河造成了極大的恐慌永品,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,509評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件击纬,死亡現(xiàn)場離奇詭異净刮,居然都是意外死亡去团,警方通過查閱死者的電腦和手機(jī)薪韩,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,806評論 3 394
  • 文/潘曉璐 我一進(jìn)店門斯嚎,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人殃饿,你說我怎么就攤上這事谋作。” “怎么了乎芳?”我有些...
    開封第一講書人閱讀 163,875評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長帖池。 經(jīng)常有香客問我奈惑,道長,這世上最難降的妖魔是什么睡汹? 我笑而不...
    開封第一講書人閱讀 58,441評論 1 293
  • 正文 為了忘掉前任肴甸,我火速辦了婚禮,結(jié)果婚禮上囚巴,老公的妹妹穿的比我還像新娘原在。我一直安慰自己,他們只是感情好彤叉,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,488評論 6 392
  • 文/花漫 我一把揭開白布庶柿。 她就那樣靜靜地躺著,像睡著了一般秽浇。 火紅的嫁衣襯著肌膚如雪浮庐。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,365評論 1 302
  • 那天柬焕,我揣著相機(jī)與錄音审残,去河邊找鬼。 笑死斑举,一個(gè)胖子當(dāng)著我的面吹牛搅轿,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播富玷,決...
    沈念sama閱讀 40,190評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼璧坟,長吁一口氣:“原來是場噩夢啊……” “哼既穆!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起沸柔,我...
    開封第一講書人閱讀 39,062評論 0 276
  • 序言:老撾萬榮一對情侶失蹤循衰,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后褐澎,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體会钝,經(jīng)...
    沈念sama閱讀 45,500評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,706評論 3 335
  • 正文 我和宋清朗相戀三年工三,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了迁酸。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,834評論 1 347
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡俭正,死狀恐怖奸鬓,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情掸读,我是刑警寧澤串远,帶...
    沈念sama閱讀 35,559評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站儿惫,受9級特大地震影響澡罚,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜肾请,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,167評論 3 328
  • 文/蒙蒙 一留搔、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧铛铁,春花似錦隔显、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,779評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至梳毙,卻和暖如春哺窄,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背账锹。 一陣腳步聲響...
    開封第一講書人閱讀 32,912評論 1 269
  • 我被黑心中介騙來泰國打工萌业, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人奸柬。 一個(gè)月前我還...
    沈念sama閱讀 47,958評論 2 370
  • 正文 我出身青樓生年,卻偏偏與公主長得像,于是被迫代替她去往敵國和親廓奕。 傳聞我的和親對象是個(gè)殘疾皇子抱婉,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,779評論 2 354

推薦閱讀更多精彩內(nèi)容

  • iOS開發(fā)中, 之前一直使用swift, 因此對于Objective-C的內(nèi)存管理機(jī)制長期處于混亂的一知半解狀態(tài)....
    icetime17閱讀 844評論 1 8
  • 內(nèi)存管理是程序在運(yùn)行時(shí)分配內(nèi)存档叔、使用內(nèi)存,并在程序完成時(shí)釋放內(nèi)存的過程蒸绩。在Objective-C中衙四,也被看作是在眾...
    蹲瓜閱讀 3,072評論 1 8
  • 1.ios高性能編程 (1).內(nèi)層 最小的內(nèi)層平均值和峰值(2).耗電量 高效的算法和數(shù)據(jù)結(jié)構(gòu)(3).初始化時(shí)...
    歐辰_OSR閱讀 29,383評論 8 265
  • 備注:本來在一年前有一個(gè)出版社找到我,讓我寫一系列關(guān)于iOS性能優(yōu)化的書患亿。但是一直因?yàn)楣ぷ髟虼福瑳]有能夠按時(shí)交付。...
    Tiberslaa閱讀 337評論 0 1
  • Swift1> Swift和OC的區(qū)別1.1> Swift沒有地址/指針的概念1.2> 泛型1.3> 類型嚴(yán)謹(jǐn) 對...
    cosWriter閱讀 11,100評論 1 32