流程圖如下
上面是我針對內(nèi)存管理的了解撩银,進(jìn)行了一個(gè)簡單的用流程的方式加以概括给涕。下面是詳細(xì)的說明一下,在面試過程中额获,針對內(nèi)存管理够庙,面試官經(jīng)常會問到哪些問題進(jìn)行剖析。
之前面試的時(shí)候抄邀,面試官都會問的問題是耘眨,你對內(nèi)存管理了解嗎?簡單說一下境肾,而我就會將上面這個(gè)圖設(shè)計(jì)到的點(diǎn)全都用手繪的形式給他講一遍剔难。這樣你就占據(jù)了主導(dǎo)位置。哈哈哈
內(nèi)存管理分為 mrc和arc兩種管理方式奥喻。
我們先從mrc說起偶宫。
MRC
規(guī)則:誰創(chuàng)建,誰釋放环鲤,誰引用纯趋,誰管理的原則。
alloc, init,new,copy-------->release
當(dāng)一個(gè)對象的retainCount = 0的時(shí)候冷离,就會調(diào)用dealloc方式吵冒,將對象釋放掉。釋放內(nèi)存的并不是release酒朵,而是dealloc桦锄。
retain是保持的意思,給一個(gè)對象發(fā)送retian消息,就意味著保持這個(gè)對象蔫耽。它的retainCount+1结耀。具體我用一張流程圖說明:
針對上面這些內(nèi)容留夜,其實(shí)可以引申出很多面試題。舉個(gè)例子??:
什么情況下使用weak關(guān)鍵字图甜,相比assgin有什么不同碍粥?
1、delegate.因?yàn)樵赼rc中黑毅,有可能會出現(xiàn)循環(huán)引用嚼摩。
2、自身已經(jīng)對它進(jìn)行一次強(qiáng)引用矿瘦,沒有必要再強(qiáng)引用一次枕面,比如IBOutlet控件屬性。
不同點(diǎn)
1缚去、weak為屬性設(shè)置新值的時(shí)候潮秘,設(shè)置方法既不保留新值,也不釋放舊值易结。在遭到摧毀時(shí)枕荞,屬性值也會被清空。assgin的設(shè)置方法只針對基本數(shù)據(jù)類型等簡單的賦值操作搞动。
2躏精、assgin可以用非OC對象,weak必須用于oc對象鹦肿。
runtime如何實(shí)現(xiàn)weak屬性矗烛、實(shí)現(xiàn)原理
這個(gè)會在后續(xù)的篇幅中重點(diǎn)介紹
@property中有哪些屬性關(guān)鍵字?/@property后面可以有哪些修飾符狮惜?
屏幕快照 2019-04-02 上午10.35.51.png
或者換一個(gè)問法:
屬性readwrite高诺,readonly,assign碾篡,retain虱而,copy,nonatomic 各是什么作用开泽,在那種情況下用?
1). readwrite 是可讀可寫特性;需要生成getter方法和setter方法時(shí)
2). readonly 是只讀特性 只會生成getter方法 不會生成setter方法 ;不希望屬性在類外改變
3). assign 是賦值特性牡拇,setter方法將傳入?yún)?shù)賦值給實(shí)例變量;僅設(shè)置變量時(shí);
4). retain 表示持有特性,setter方法將傳入?yún)?shù)先保留穆律,再賦值惠呼,傳入?yún)?shù)的retaincount會+1;
5). copy 表示賦值特性,setter方法將傳入對象復(fù)制一份;需要完全一份新的變量時(shí)峦耘。
6).nonatomic 非原子操作剔蹋,決定編譯器生成的setter getter是否是原子操作,atomic表示多線程安全辅髓,一般使用nonatomic
weak屬性在dealloc中置nil么泣崩?
不需要少梁。
在ARC中環(huán)境無論是強(qiáng)指針還是弱指針都無需在dealloc設(shè)置為Nil,ARC會自動幫我們處理。
在屬性所指的對象遭到摧毀時(shí)矫付,屬性值也會清空凯沪。
- (void)setObject:(NSObject *)object{
objc_setAssociatedObject(self, @"object", object, OBJC_ASSOCIATION_ASSIGN);
[object cyl_runAtDealloc:^{
_object = nil;
}];
}
關(guān)于copy關(guān)鍵字的深淺拷貝等面試題,會在后面提到买优。我們繼續(xù)說一下內(nèi)存管理妨马。
ARC
還是上面那張關(guān)于引用計(jì)數(shù)的流程圖。
autorelease
實(shí)現(xiàn)杀赢、本質(zhì)烘跺、RunLoop相關(guān)
·App啟動后,蘋果在主線程 RunLoop 里注冊了兩個(gè) Observer葵陵,其回調(diào)都是 _wrapRunLoopWithAutoreleasePoolHandler()液荸。
·第一個(gè) Observer 監(jiān)視的事件是 Entry(即將進(jìn)入Loop),其回調(diào)內(nèi)會調(diào)用 _objc_autoreleasePoolPush() 創(chuàng)建自動釋放池脱篙。其 order 是-2147483647,優(yōu)先級最高伤柄,保證創(chuàng)建釋放池發(fā)生在其他所有回調(diào)之前绊困。
·第二個(gè) Observer 監(jiān)視了兩個(gè)事件: BeforeWaiting(準(zhǔn)備進(jìn)入休眠) 時(shí)調(diào)用_objc_autoreleasePoolPop() 和 _objc_autoreleasePoolPush() 釋放舊的池并創(chuàng)建新池;Exit(即將退出Loop) 時(shí)調(diào)用 _objc_autoreleasePoolPop() 來釋放自動釋放池适刀。這個(gè) Observer 的 order 是 2147483647秤朗,優(yōu)先級最低,保證其釋放池子發(fā)生在其他所有回調(diào)之后笔喉。
·在主線程執(zhí)行的代碼取视,通常是寫在諸如事件回調(diào)、Timer回調(diào)內(nèi)的常挚。這些回調(diào)會被 RunLoop 創(chuàng)建好的 AutoreleasePool 環(huán)繞著作谭,所以不會出現(xiàn)內(nèi)存泄漏,開發(fā)者也不必顯示創(chuàng)建 Pool 了奄毡。
也就是說AutoreleasePool創(chuàng)建是在一個(gè)RunLoop事件開始之前(push)折欠,AutoreleasePool釋放是在一個(gè)RunLoop事件即將結(jié)束之前(pop)。
AutoreleasePool里的Autorelease對象的加入是在RunLoop事件中吼过,AutoreleasePool里的Autorelease對象的釋放是在AutoreleasePool釋放時(shí)锐秦。
具體見這篇文章,非常清楚
AutoreleasePool底層實(shí)現(xiàn)原理剖析
問:
1盗忱、談?wù)勀銓ψ詣俞尫懦氐睦斫?br>
2酱床、多層自動釋放池嵌套的對象在哪一層釋放。
自動釋放池是oc提供的一種自動回收的機(jī)制趟佃,具有延遲釋放的特性扇谣,即當(dāng)我們創(chuàng)建了一個(gè)對象慷垮,并把他加入到了自動釋放池中時(shí),他不會立即被釋放揍堕,會等到一次runloop結(jié)束或者作用域超出{}或者超出[pool release]之后再被釋放料身。
Objective-C如何對內(nèi)存管理的,說說你的看法和解決方法?
答:Objective-C的內(nèi)存管理主要有三種方式ARC(自動內(nèi)存計(jì)數(shù))、手動內(nèi)存計(jì)數(shù)衩茸、內(nèi)存池芹血。
1). (Garbage Collection)自動內(nèi)存計(jì)數(shù):這種方式和java類似,在你的程序的執(zhí)行過程中楞慈。始終有一個(gè)高人在背后準(zhǔn)確地幫你收拾垃圾幔烛,你不用考慮它什么時(shí)候開始工作,怎樣工作囊蓝。你只需要明白饿悬,我申請了一段內(nèi)存空間,當(dāng)我不再使用從而這段內(nèi)存成為垃圾的時(shí)候聚霜,我就徹底的把它忘記掉狡恬,反正那個(gè)高人會幫我收拾垃圾。遺憾的是蝎宇,那個(gè)高人需要消耗一定的資源弟劲,在攜帶設(shè)備里面,資源是緊俏商品所以iPhone不支持這個(gè)功能姥芥。所以“Garbage Collection”不是本入門指南的范圍兔乞,對“Garbage Collection”內(nèi)部機(jī)制感興趣的同學(xué)可以參考一些其他的資料,不過說老實(shí)話“Garbage Collection”不大適合適初學(xué)者研究凉唐。
解決方法: 通過alloc – initial方式創(chuàng)建的, 創(chuàng)建后引用計(jì)數(shù)+1, 此后每retain一次引用計(jì)數(shù)+1, 那么在程序中做相應(yīng)次數(shù)的release就好了.
2). (Reference Counted)手動內(nèi)存計(jì)數(shù):就是說庸追,從一段內(nèi)存被申請之后,就存在一個(gè)變量用于保存這段內(nèi)存被使用的次數(shù)台囱,我們暫時(shí)把它稱為計(jì)數(shù)器淡溯,當(dāng)計(jì)數(shù)器變?yōu)?的時(shí)候,那么就是釋放這段內(nèi)存的時(shí)候玄坦。比如說血筑,當(dāng)在程序A里面一段內(nèi)存被成功申請完成之后,那么這個(gè)計(jì)數(shù)器就從0變成1(我們把這個(gè)過程叫做alloc)煎楣,然后程序B也需要使用這個(gè)內(nèi)存豺总,那么計(jì)數(shù)器就從1變成了2(我們把這個(gè)過程叫做retain)。緊接著程序A不再需要這段內(nèi)存了择懂,那么程序A就把這個(gè)計(jì)數(shù)器減1(我們把這個(gè)過程叫做release);程序B也不再需要這段內(nèi)存的時(shí)候喻喳,那么也把計(jì)數(shù)器減1(這個(gè)過程還是release)。當(dāng)系統(tǒng)(也就是Foundation)發(fā)現(xiàn)這個(gè)計(jì)數(shù)器變 成員了0困曙,那么就會調(diào)用內(nèi)存回收程序把這段內(nèi)存回收(我們把這個(gè)過程叫做dealloc)表伦。順便提一句谦去,如果沒有Foundation,那么維護(hù)計(jì)數(shù)器蹦哼,釋放內(nèi)存等等工作需要你手工來完成鳄哭。
解決:一般是由類的靜態(tài)方法創(chuàng)建的, 函數(shù)名中不會出現(xiàn)alloc或init字樣, 如[NSString string]和[NSArray arrayWithObject:], 創(chuàng)建后引用計(jì)數(shù)+0, 在函數(shù)出棧后釋放, 即相當(dāng)于一個(gè)棧上的局部變量. 當(dāng)然也可以通過retain延長對象的生存期.
3). (NSAutoRealeasePool)內(nèi)存池:可以通過創(chuàng)建和釋放內(nèi)存池控制內(nèi)存申請和回收的時(shí)機(jī).
解決:是由autorelease加入系統(tǒng)內(nèi)存池, 內(nèi)存池是可以嵌套的, 每個(gè)內(nèi)存池都需要有一個(gè)創(chuàng)建釋放對, 就像main函數(shù)中寫的一樣. 使用也很簡單, 比如[[[NSString alloc]initialWithFormat:@”Hey you!”] autorelease], 即將一個(gè)NSString對象加入到最內(nèi)層的系統(tǒng)內(nèi)存池, 當(dāng)我們釋放這個(gè)內(nèi)存池時(shí), 其中的對象都會被釋放.
內(nèi)存管理的幾條原則時(shí)什么?按照默認(rèn)法則.那些關(guān)鍵字生成的對象需要手動釋放?在和property結(jié)合的時(shí)候怎樣有效的避免內(nèi)存泄露?
答:誰申請,誰釋放
遵循Cocoa Touch的使用原則;
內(nèi)存管理主要要避免“過早釋放”和“內(nèi)存泄漏”纲熏,對于“過早釋放”需要注意@property設(shè)置特性時(shí)妆丘,一定要用對特性關(guān)鍵字,對于“內(nèi)存泄漏”局劲,一定要申請了要負(fù)責(zé)釋放勺拣,要細(xì)心。
關(guān)鍵字alloc 或new 生成的對象需要手動釋放;
設(shè)置正確的property屬性鱼填,對于retain需要在合適的地方釋放药有,