iOS 內(nèi)存管理相關(guān)面試題

為什么要使用內(nèi)存管理?

嚴(yán)格的內(nèi)存管理,能夠是我們的應(yīng)用程在性能上有很大的提高
如果忽略內(nèi)存管理,可能導(dǎo)致應(yīng)用占用內(nèi)存過(guò)高,導(dǎo)致程序崩潰
OC的內(nèi)存管理主要有三種方式:

ARC(自動(dòng)內(nèi)存計(jì)數(shù))
手動(dòng)內(nèi)存計(jì)數(shù)
內(nèi)存池
OC中內(nèi)存管理的基本思想:

保證任何時(shí)候指向?qū)ο蟮闹羔槀€(gè)數(shù)和對(duì)象的引用計(jì)數(shù)相同,多一個(gè)指針指向這個(gè)對(duì)象這個(gè)對(duì)象的引用計(jì)數(shù)就加1,少一個(gè)指針指向這個(gè)對(duì)象這個(gè)對(duì)象的引用計(jì)數(shù)就減1。沒有指針指向這個(gè)對(duì)象對(duì)象就被釋放了。

每個(gè)對(duì)象都有一個(gè)引用計(jì)數(shù)器,每個(gè)新對(duì)象的計(jì)數(shù)器是1,當(dāng)對(duì)象的計(jì)數(shù)器減為0時(shí),就會(huì)被銷毀
通過(guò)retain可以讓對(duì)象的計(jì)數(shù)器+1、release可以讓對(duì)象的計(jì)數(shù)器-1
還可以通過(guò)autorelease pool管理內(nèi)存
如果用ARC,編譯器會(huì)自動(dòng)生成管理內(nèi)存的代碼
蘋果官方基礎(chǔ)內(nèi)存管理規(guī)則:

你擁有你創(chuàng)建的任何對(duì)象
你可以使用retain獲取一個(gè)對(duì)象的擁有權(quán)
當(dāng)你不再需要它,你必須放棄你擁有的對(duì)象的擁有權(quán)
你一定不能釋放不是你擁有的對(duì)象的擁有權(quán)
自動(dòng)內(nèi)存管理

談?wù)勀銓?duì) ARC 的認(rèn)識(shí)和理解? ARC 是iOS 5推出的新功能。編譯器在代碼里適當(dāng)?shù)牡胤阶詣?dòng)插入 retain / release 完成內(nèi)存管理(引用計(jì)數(shù))。

ARC機(jī)制中,系統(tǒng)判斷對(duì)象是否被銷毀的依據(jù)是什么?

指向?qū)ο蟮膹?qiáng)指針是否被銷毀

引用計(jì)數(shù)器

給對(duì)象發(fā)送一條retain消息,可以使引用計(jì)數(shù)器+1(retain方法返回對(duì)象本身)
給對(duì)象發(fā)送一條release消息,可以使引用計(jì)數(shù)器-1(注意release并不代表銷毀/回收對(duì)象,僅僅是計(jì)數(shù)器-1)
給對(duì)象發(fā)送retainCount消息,可以獲得當(dāng)前的引用計(jì)數(shù)值
自動(dòng)釋放池

自動(dòng)釋放池底層怎么實(shí)現(xiàn)?

(以棧的方式實(shí)現(xiàn)的)(系統(tǒng)自動(dòng)創(chuàng)建,系統(tǒng)自動(dòng)釋放)棧里面的(先進(jìn)后出)

內(nèi)存里面有棧,棧里面有自動(dòng)釋放池。

自動(dòng)釋放池以棧的形式實(shí)現(xiàn):當(dāng)你創(chuàng)建一個(gè)新的自動(dòng)釋放池時(shí),它將被添加到棧頂。當(dāng)一個(gè)對(duì)象收到發(fā)送autorelease消息時(shí),它被添加到當(dāng)前線程的處于棧頂?shù)淖詣?dòng)釋放池中,當(dāng)自動(dòng)釋放池被回收時(shí),它們從棧中被刪除,并且會(huì)給池子里面所有的對(duì)象都會(huì)做一次release操作。

什么是自動(dòng)釋放池?

答:自動(dòng)釋放池是用來(lái)存儲(chǔ)多個(gè)對(duì)象類型的指針變量

自動(dòng)釋放池對(duì)池內(nèi)對(duì)象的作用? 被存入到自動(dòng)釋放池內(nèi)的對(duì)象,當(dāng)自動(dòng)釋放池被銷毀時(shí),會(huì)對(duì)池內(nèi)的對(duì)象全部做一次release操作

對(duì)象如何放入到自動(dòng)釋放池中? 當(dāng)你確定要將對(duì)象放入到池中的時(shí)候,只需要調(diào)用對(duì)象的 autorelease 對(duì)象方法就可以把對(duì)象放入到自動(dòng)釋放池中

多次調(diào)用對(duì)象的autorelease方法會(huì)導(dǎo)致什么問(wèn)題?

答:多次將地址存到自動(dòng)釋放池中,導(dǎo)致野指針異常

自動(dòng)釋放池作用

將對(duì)象與自動(dòng)釋放池建立關(guān)系,池子內(nèi)調(diào)用 autorelease 方法,在自動(dòng)釋放池銷毀時(shí)銷毀對(duì)象,延遲 release 銷毀時(shí)間

自動(dòng)釋放池,什么時(shí)候創(chuàng)建?

程序剛啟動(dòng)的時(shí)候,也會(huì)創(chuàng)建一個(gè)自動(dòng)釋放池
產(chǎn)生事件以后,運(yùn)行循環(huán)開始處理事件,就會(huì)創(chuàng)建自動(dòng)釋放池
什么時(shí)候銷毀的?

程序運(yùn)行結(jié)束之前銷毀
事件處理結(jié)束以后,會(huì)銷毀自動(dòng)釋放池
還有在池子滿的時(shí)候,也會(huì)銷毀
自動(dòng)釋放池使用注意:

不要把大量循環(huán)操作放在釋放池下,因?yàn)檫@會(huì)導(dǎo)致大量循環(huán)內(nèi)的對(duì)象沒有被回收,這種情況下應(yīng)該手動(dòng)寫 release 代碼。盡量避免對(duì)大內(nèi)存對(duì)象使用 autorelease ,否則會(huì)延遲大內(nèi)存的回收。

autorelease的對(duì)象是在什么時(shí)候被release的?

答:autorelease實(shí)際上只是把對(duì)release的調(diào)用延遲了,對(duì)于每一個(gè)Autorelease,系統(tǒng)只是把該Object放入了當(dāng)前的 Autoreleasepool中,當(dāng)該pool被釋放時(shí),該pool中的所有Object會(huì)被調(diào)用Release。對(duì)于每一個(gè)Runloop,系統(tǒng)會(huì)隱式創(chuàng)建一個(gè)Autoreleasepool,這樣所有的releasepool會(huì)構(gòu)成一個(gè)象CallStack一樣的一個(gè)棧式結(jié)構(gòu),在每一個(gè) Runloop結(jié)束時(shí),當(dāng)前棧頂?shù)腁utoreleasepool會(huì)被銷毀,這樣這個(gè)pool里的每個(gè)Object(就是autorelease的對(duì)象)會(huì)被release。那什么是一個(gè)Runloop呢?一個(gè)UI事件,Timer call,delegate call, 都會(huì)是一個(gè)新的Runloop。

If we don’t create any autorelease pool in our application then is there any autorelease pool already provided to us?

系統(tǒng)會(huì)默認(rèn)會(huì)不定時(shí)地創(chuàng)建和銷毀自動(dòng)釋放池

When you will create an autorelease pool in your application?

當(dāng)不需要精確地控制對(duì)象的釋放時(shí)間時(shí),可以手動(dòng)創(chuàng)建自動(dòng)釋放池

@property內(nèi)存管理策略的選擇

讀寫屬性:readwrite 、readonly setter語(yǔ)意:assign 、retain/copy 原子性(多線程管理):atomic 、 nonatomic 強(qiáng)弱引用:strong 、 weak

讀寫屬性:

readwrite :同時(shí)生成 set 和 get 方法(默認(rèn))

readonly :只會(huì)生成 get 方法

控制set方法的內(nèi)存管理:

retain : release 舊值, retain 新值。希望獲得源對(duì)象的所有權(quán)時(shí),對(duì)其他 NSObject 和其子類(用于 OC 對(duì)象)

copy : release 舊值, copy 新值。希望獲得源對(duì)象的副本而不改變?cè)磳?duì)象內(nèi)容時(shí)(一般用于 NSString , block )

assign :直接賦值,不做任何內(nèi)存管理(默認(rèn)屬性),控制需不需生成 set 方法。對(duì)基礎(chǔ)數(shù)據(jù)類型 ( NSInteger , CGFloat )和C數(shù)據(jù)類型( int , float , double , char , 等等)

原子性(多線程管理):

atomic
默認(rèn)屬性,訪問(wèn)方法都為原子型事務(wù)訪問(wèn)。鎖被加到所屬對(duì)象實(shí)例級(jí),性能低。原子性就是說(shuō)一個(gè)操作不可以中途被 cpu 暫停然后調(diào)度, 即不能被中斷, 要不就執(zhí)行完, 要不就不執(zhí)行. 如果一個(gè)操作是原子性的,那么在多線程環(huán)境下, 就不會(huì)出現(xiàn)變量被修改等奇怪的問(wèn)題。原子操作就是不可再分的操作,在多線程程序中原子操作是一個(gè)非常重要的概念,它常常用來(lái)實(shí)現(xiàn)一些同步機(jī)制,同時(shí)也是一些常見的多線程 Bug 的源頭。當(dāng)然,原子性的變量在執(zhí)行效率上要低些。
nonatomic
非原子性訪問(wèn)。不加同步,盡量避免多線程搶奪同一塊資源。是直接從內(nèi)存中取數(shù)值,因?yàn)樗菑膬?nèi)存中取得數(shù)據(jù),它并沒有一個(gè)加鎖的保護(hù)來(lái)用于cpu中的寄存器計(jì)算Value,它只是單純的從內(nèi)存地址中,當(dāng)前的內(nèi)存存儲(chǔ)的數(shù)據(jù)結(jié)果來(lái)進(jìn)行使用。 多線程并發(fā)訪問(wèn)會(huì)提高性能,但無(wú)法保證數(shù)據(jù)同步。盡量避免多線程搶奪同一塊資源,否則盡量將加鎖資源搶奪的業(yè)務(wù)邏輯交給服務(wù)器處理,減少移動(dòng)客戶端的壓力。
當(dāng)有多個(gè)線程需要訪問(wèn)到同一個(gè)數(shù)據(jù)時(shí),OC中,我們可以使用 @synchronized (變量)來(lái)對(duì)該變量進(jìn)行加鎖(加鎖的目的常常是為了同步或保證原子操作)。
強(qiáng)指針(strong)、弱指針(weak)

strong
strong 系統(tǒng)一般不會(huì)自動(dòng)釋放,在 oc 中,對(duì)象默認(rèn)為強(qiáng)指針。作用域銷毀時(shí)銷毀引用。在實(shí)際開放中一般屬性對(duì)象一般 strong 來(lái)修飾( NSArray , NSDictionary ),在使用懶加載定義控件的時(shí)候,一般也用strong。
weak
weak 所引用對(duì)象的計(jì)數(shù)器不會(huì)加一,當(dāng)對(duì)象被釋放時(shí)指針會(huì)被自動(dòng)賦值為 nil ,系統(tǒng)會(huì)立刻釋放對(duì)象。
__unsafe_unretained 弱引用 當(dāng)對(duì)象被釋放時(shí)指針不會(huì)被自動(dòng)賦值為 ni
在ARC時(shí)屬性的修飾符是可以用 assign 的(相當(dāng)于 __unsafe_unretained )
在ARC時(shí)屬性的修飾符是可以用 retain 的 (相當(dāng)于 __strong )
假定有N個(gè)指針指向同一個(gè)對(duì)象,如果至少有一個(gè)是強(qiáng)引用,這個(gè)對(duì)象只要還在作用域內(nèi)就不會(huì)被釋放。相反,如果這N個(gè)指針都是弱引用,這個(gè)對(duì)象馬上就被釋放
在使用 sb 或者 xib 給控件拖線的時(shí)候,為什么拖出來(lái)的先屬性都是用 weak 修飾呢?
由于在向 xib 或者 sb 里面添加控件的時(shí)候,添加的子視圖是添加到了跟視圖 View 上面,而 控制器 Controller 對(duì)其根視圖 View 默認(rèn)是強(qiáng)引用的,當(dāng)我們的子控件添加到 view 上面的時(shí)候, self.view addSubView: 這個(gè)方法會(huì)對(duì)添加的控件進(jìn)行強(qiáng)引用,如果在用 strong 對(duì)添加的子控件進(jìn)行修飾的話,相當(dāng)于有兩條強(qiáng)指針對(duì)子控件進(jìn)行強(qiáng)引用, 為了避免這種情況,所以用 weak 修飾。
注意:
(1)addSubView 默認(rèn)對(duì)其 subView 進(jìn)行了強(qiáng)引用
(2)在純手碼實(shí)現(xiàn)界面布局時(shí),如果通過(guò)懶加載處理界面控件,需要使用strong強(qiáng)指針
ARC管理內(nèi)存是用 assign 還是用 weak ?

assign : 如果由于某些原因代理對(duì)象被釋放了,代理指針就變成了野指針。

weak : 如果由于某些原因代理對(duì)象被釋放了,代理指針就變成了空指針,更安全( weak 不能修飾基本數(shù)據(jù)類型,只能修飾對(duì)象)。

內(nèi)存分析

靜態(tài)分析(Analyze)
不運(yùn)行程序, 直接檢測(cè)代碼中是否有潛在的內(nèi)存問(wèn)題(不一定百分百準(zhǔn)確, 僅僅是提供建議)
結(jié)合實(shí)際情況來(lái)分析, 是否真的有內(nèi)存問(wèn)題
動(dòng)態(tài)分析(Profile == Instruments)
運(yùn)行程序, 通過(guò)使用app,查看內(nèi)存的分配情況(Allocations):可以查看做出了某個(gè)操作后(比如點(diǎn)擊了某個(gè)按鈕\顯示了某個(gè)控制器),內(nèi)存是否有暴增的情況(突然變化)
運(yùn)行程序, 通過(guò)使用app, 查看是否有內(nèi)存泄漏(Leaks):紅色區(qū)域代表內(nèi)存泄漏出現(xiàn)的地方
什么情況下會(huì)發(fā)生內(nèi)存泄漏和內(nèi)存溢出?

內(nèi)存泄漏:堆里不再使用的對(duì)象沒有被銷毀,依然占據(jù)著內(nèi)存。

內(nèi)存溢出:一次內(nèi)存泄露危害可以忽略,但內(nèi)存泄露多了,內(nèi)存遲早會(huì)被占光,最終會(huì)導(dǎo)致內(nèi)存溢出!當(dāng)程序在申請(qǐng)內(nèi)存時(shí),沒有足夠的內(nèi)存空間供其使用,出現(xiàn)out of memory;比如數(shù)據(jù)長(zhǎng)度比較小的數(shù)據(jù)類型 存儲(chǔ)了數(shù)據(jù)長(zhǎng)度比較大的數(shù)據(jù)。

關(guān)于圖片占用內(nèi)存管理

圖片加載占用內(nèi)存對(duì)比

使用 imageName: 加載圖片:
加載到內(nèi)存當(dāng)中后,占據(jù)內(nèi)存空間較大
相同的圖片,圖片不會(huì)重復(fù)加載
加載內(nèi)存當(dāng)中之后,會(huì)一直停留在內(nèi)存當(dāng)中,不會(huì)隨著對(duì)象銷毀而銷毀
加載進(jìn)去圖片之后,占用的內(nèi)存歸系統(tǒng)管理,我們無(wú)法管理
使用 imageWithContentsOfFile: 加載圖片
加載到內(nèi)存當(dāng)中后,占據(jù)內(nèi)存空間較小
相同的圖片會(huì)被重復(fù)加載內(nèi)存當(dāng)中
對(duì)象銷毀的時(shí)候,加載到內(nèi)存中圖片會(huì)隨著一起銷毀
結(jié)論:
imageName:
imageWithContentsOfFile:
圖片在沙盒中的存在形式

部署版本在>=iOS8的時(shí)候,打包的資源包中的圖片會(huì)被放到Assets.car。圖片有被壓縮;
部署版本在<iOS8的時(shí)候,打包的資源包中的圖片會(huì)被放在MainBudnle里面。圖片沒有被壓縮
沒有放在Images.xcassets里面的所有圖片會(huì)直接暴露在沙盒的資源包(main Bundle), 不會(huì)壓縮到Assets.car文件,會(huì)被放到MainBudnle里面。圖片沒有被壓縮
結(jié)論:
小圖片\使用頻率比較高的圖片放在Images.xcassets里面
大圖片\使用頻率比較低的圖片(一次性的圖片, 比如版本新特性的圖片)不要放在Images.xcassets里面
內(nèi)存管理問(wèn)題

單個(gè)對(duì)象內(nèi)存管理的問(wèn)題

關(guān)于內(nèi)存我們主要研究的問(wèn)題是什么? 野指針:對(duì)象的retainCount已經(jīng)為0,保存了對(duì)象指針地址的變量就是野指針。使用野指針調(diào)用對(duì)象的方法,會(huì)導(dǎo)致野指針異常,導(dǎo)致程序直接崩潰
內(nèi)存泄露:已經(jīng)不在使用的對(duì)象,沒有正確的釋放掉,一直駐留在內(nèi)存中,我們就說(shuō)是內(nèi)存泄漏
僵尸對(duì)象? retainCount = 0的對(duì)象被稱之為僵尸對(duì)象,也就是不能夠在訪問(wèn)的對(duì)象
是什么問(wèn)題導(dǎo)致,訪問(wèn)僵尸對(duì)象,時(shí)而正確時(shí)而錯(cuò)誤?
如何開始xcode的時(shí)時(shí)檢測(cè)僵尸對(duì)象功能?
當(dāng)對(duì)象的retainCount = 0 時(shí) 能否調(diào)用 retain方法使對(duì)象復(fù)活? 已經(jīng)被釋放的對(duì)象是無(wú)法在復(fù)活的
如何防止出現(xiàn)野指針操作? 通常在調(diào)用完release方法后,會(huì)把保存了對(duì)象指針地址的變量清空,賦值為nil 在oc中沒有空指針異常,所以使用[nil retain]調(diào)用方法不會(huì)導(dǎo)致異常的發(fā)生
內(nèi)存泄漏有幾種情況?
沒有配對(duì)釋放,不符合內(nèi)存管理原則
對(duì)象提前賦值為nil或者清空,導(dǎo)致release方法沒有起作用
多個(gè)對(duì)象內(nèi)存管理的問(wèn)題

對(duì)象與對(duì)象之間存在幾種關(guān)系?
繼承關(guān)系
組合關(guān)系
對(duì)象作為方法參數(shù)傳遞
對(duì)象的組合關(guān)系中,如何確保作為成員變量的對(duì)象,不會(huì)被提前釋放? 重寫set方法,在set方法中,retain該對(duì)像,使其retainCount值增加 1
組合關(guān)系導(dǎo)致內(nèi)存泄漏的原因是什么? 在set方法中,retain了該對(duì)象,但是并沒有配對(duì)釋放
作為成員變量的對(duì)象,應(yīng)該在那里配對(duì)釋放? 在dealloc函數(shù)中釋放
內(nèi)存相關(guān)的一些數(shù)據(jù)結(jié)構(gòu)的對(duì)比

簡(jiǎn)述內(nèi)存分區(qū)情況

代碼區(qū):存放函數(shù)二進(jìn)制代碼
數(shù)據(jù)區(qū):系統(tǒng)運(yùn)行時(shí)申請(qǐng)內(nèi)存并初始化,系統(tǒng)退出時(shí)由系統(tǒng)釋放。存放全局變量、靜態(tài)變量、常量
堆區(qū):通過(guò)malloc等函數(shù)或new等操作符動(dòng)態(tài)申請(qǐng)得到,需程序員手動(dòng)申請(qǐng)和釋放
棧區(qū):函數(shù)模塊內(nèi)申請(qǐng),函數(shù)結(jié)束時(shí)由系統(tǒng)自動(dòng)釋放。存放局部變量、函數(shù)參數(shù)
手機(jī)的存儲(chǔ)空間分為內(nèi)存(RAM)和閃存(Flash)兩種

內(nèi)存一般較小:1G、2G、3G、4G。閃存空間相對(duì)較大16G、32G、64G;
內(nèi)存的讀寫速度較快、閃存的讀寫速度相對(duì)較慢;
內(nèi)存里的東西掉電后全部丟失、閃存里的東西掉電也不丟;
內(nèi)存相當(dāng)于電腦的內(nèi)存條、閃存相當(dāng)于電腦的硬盤;
堆和棧的區(qū)別?

管理方式:
堆釋放工作由程序員控制,容易產(chǎn)生memory leak;
棧是由編譯器自動(dòng)管理,無(wú)需我們手工控制。
申請(qǐng)大小:
堆:堆是向高地址擴(kuò)展的數(shù)據(jù)結(jié)構(gòu),是不連續(xù)的內(nèi)存區(qū)域。這是由于系統(tǒng)是用鏈表來(lái)存儲(chǔ)的空閑內(nèi)存地址的,自然是不連續(xù)的,而鏈表的遍歷方向是由低地址向高地址。堆的大小受限于計(jì)算機(jī)系統(tǒng)中有效的虛擬內(nèi)存。由此可見,堆獲得的空間比較靈活,也比較大。
棧:在Windows下,棧是向低地址擴(kuò)展的數(shù)據(jù)結(jié)構(gòu),是一塊連續(xù)的內(nèi)存的區(qū)域。這句話的意思是棧頂?shù)牡刂泛蜅5淖畲笕萘渴窍到y(tǒng)預(yù)先規(guī)定好的,在 Windows下,棧的大小是2M(也有的說(shuō)是1M,總之是一個(gè)編譯時(shí)就確定的常數(shù)),如果申請(qǐng)的空間超過(guò)棧的剩余空間時(shí),將提示overflow。因此,能從棧獲得的空間較小。
碎片問(wèn)題:
堆:頻繁的new/delete勢(shì)必會(huì)造成內(nèi)存空間的不連續(xù),從而造成大量的碎片,使程序效率降低。
棧:則不會(huì)存在這個(gè)問(wèn)題,因?yàn)闂J窍冗M(jìn)后出的隊(duì)列,他們是如此的一一對(duì)應(yīng),以至于永遠(yuǎn)都不可能有一個(gè)內(nèi)存塊從棧中間彈出
分配方式:
堆都是動(dòng)態(tài)分配的,沒有靜態(tài)分配的堆。
棧有2種分配方式:靜態(tài)分配和動(dòng)態(tài)分配。靜態(tài)分配是編譯器完成的,比如局部變量的分配。動(dòng)態(tài)分配由alloc函數(shù)進(jìn)行分配,但是棧的動(dòng)態(tài)分配和堆是不同的,他的動(dòng)態(tài)分配是由編譯器進(jìn)行釋放,無(wú)需我們手工實(shí)現(xiàn)。
分配效率:
棧:是機(jī)器系統(tǒng)提供的數(shù)據(jù)結(jié)構(gòu),計(jì)算機(jī)會(huì)在底層對(duì)棧提供支持:分配專門的寄存器存放棧的地址,壓棧出棧都有專門的指令執(zhí)行,這就決定了棧的效率比較高。
堆:則是C/C++函數(shù)庫(kù)提供的,它的機(jī)制是很復(fù)雜的。
每個(gè)App有個(gè)內(nèi)存空間,假定是4G,分為堆和棧兩大部分。一般來(lái)說(shuō)每個(gè)進(jìn)程有一個(gè)堆(這個(gè)進(jìn)程的所有線程共用這個(gè)堆),進(jìn)程中的線程有自己棧。
通過(guò)alloc、new或malloc獲得的內(nèi)存在堆中分配,堆中的內(nèi)存需要寫相應(yīng)的代碼釋放。如果進(jìn)程結(jié)束了在堆中分配的內(nèi)存會(huì)自動(dòng)釋放。
局部變量、函數(shù)參數(shù)是在??臻g中分配,如果函數(shù)返回這個(gè)函數(shù)中的局部變量、參數(shù)所占的內(nèi)存系統(tǒng)自動(dòng)釋放(回收)。
程序在編譯期對(duì)變量和函數(shù)分配內(nèi)存都在棧上進(jìn)行,且程序運(yùn)行過(guò)程中函數(shù)調(diào)用時(shí)參數(shù)的傳遞也在棧上進(jìn)行。
隊(duì)列和棧有什么區(qū)別:

隊(duì)列和棧是兩種不同的數(shù)據(jù)容器。從”數(shù)據(jù)結(jié)構(gòu)”的角度看,它們都是線性結(jié)構(gòu),即數(shù)據(jù)元素之間的關(guān)系相同。

隊(duì)列是一種先進(jìn)先出的數(shù)據(jù)結(jié)構(gòu),它在兩端進(jìn)行操作,一端進(jìn)行入隊(duì)列操作,一端進(jìn)行出列隊(duì)操作。

棧是一種先進(jìn)后出的數(shù)據(jù)結(jié)構(gòu),它只能在棧頂進(jìn)行操作,入棧和出棧都在棧頂操作。

鏈表和數(shù)組的區(qū)別在哪里?

二者都屬于一種數(shù)據(jù)結(jié)構(gòu)。如果需要快速訪問(wèn)數(shù)據(jù),很少或不插入和刪除元素,就應(yīng)該用數(shù)組;相反, 如果需要經(jīng)常插入和刪除元素就需要用鏈表數(shù)據(jù)結(jié)構(gòu)。

從邏輯結(jié)構(gòu)來(lái)看
數(shù)組必須事先定義固定的長(zhǎng)度(元素個(gè)數(shù)),不能適應(yīng)數(shù)據(jù)動(dòng)態(tài)地增減的情況。當(dāng)數(shù)據(jù)增加時(shí),可能超出原先定義的元素個(gè)數(shù);當(dāng)數(shù)據(jù)減少時(shí),造成內(nèi)存浪費(fèi);數(shù)組可以根據(jù)下標(biāo)直接存取。
鏈表動(dòng)態(tài)地進(jìn)行存儲(chǔ)分配,可以適應(yīng)數(shù)據(jù)動(dòng)態(tài)地增減的情況,且可以方便地插入、刪除數(shù)據(jù)項(xiàng)。(數(shù)組中插入、刪除數(shù)據(jù)項(xiàng)時(shí),需要移動(dòng)其它數(shù)據(jù)項(xiàng),非常繁瑣)鏈表必須根據(jù)next指針找到下一個(gè)元素
從內(nèi)存存儲(chǔ)來(lái)看
數(shù)組從棧中分配空間,對(duì)于程序員方便快速,但是自由度小
鏈表從堆中分配空間, 自由度大但是申請(qǐng)管理比較麻煩
面試題

如何讓程序盡量減少內(nèi)存泄漏

非ARC
Foundation 對(duì)象( OC 對(duì)象) : 只要方法中包含了 alloc\new\copy\mutableCopy\retain 等關(guān)鍵字,那么這些方法產(chǎn)生的對(duì)象, 就必須在不再使用的時(shí)候調(diào)用1次 release 或者1次 autorelease 。
CoreFoundation 對(duì)象( C 對(duì)象) : 只要函數(shù)中包含了 create\new\copy\retain 等關(guān)鍵字, 那么這些方法產(chǎn)生的對(duì)象, 就必須在不再使用的時(shí)候調(diào)用1次 CFRelease 或者其他 release 函數(shù)。
ARC(只自動(dòng)管理OC對(duì)象, 不會(huì)自動(dòng)管理C語(yǔ)言對(duì)象)
CoreFoundation 對(duì)象( C 對(duì)象) : 只要函數(shù)中包含了 create\new\copy\retain 等關(guān)鍵字, 那么這些方法產(chǎn)生的對(duì)象, 就必須在不再使用的時(shí)候調(diào)用1次 CFRelease 或者其他 release 函數(shù)。
block的注意

// block的內(nèi)存默認(rèn)在棧里面(系統(tǒng)自動(dòng)管理)
void (^test)() = ^{

};
// 如果對(duì)block進(jìn)行了Copy操作, block的內(nèi)存會(huì)遷移到堆里面(需要通過(guò)代碼管理內(nèi)存)
Block_copy(test);
// 在不需要使用block的時(shí)候, 應(yīng)該做1次release操作
Block_release(test);
[test release];
復(fù)制代碼
野指針舉例

建了個(gè)視圖控制器(ARC時(shí))某個(gè)函數(shù)里寫了如下代碼。當(dāng)這個(gè)函數(shù)返回時(shí)因?yàn)闆]有指針指向b所以b會(huì)被釋放、但是b.view不會(huì)被釋放。如果在b里有需要操作b的地方(比如代理的方法),就會(huì)產(chǎn)生野指針(提前釋放)

B *b = [[B alloc]init];
[self.view addSubview:b.view];
復(fù)制代碼
set方法

在對(duì)象的組合關(guān)系中,導(dǎo)致內(nèi)存泄漏有幾種情況? 1.set方法中沒有retain對(duì)象 2.沒有release掉舊的對(duì)象 3.沒有判斷向set方法中傳入的是否是同一個(gè)對(duì)象
該如何正確的重寫set方法? 1.先判斷是否是同一個(gè)對(duì)象 2.release一次舊的對(duì)象 3.retain新的對(duì)象
寫一個(gè)setter方法用于完成@property (nonatomic,retain)NSString *name,
寫一個(gè)setter方法用于完成@property(nonatomic,copy)NSString *name。
@property (nonatomic, retain) NSString *name;

  • (void)setName:(NSString *)name {
    if (_name != name) {
    [_name release];
    _name = [name retain];
    }
    }

@property(nonatomic, copy) NSString *name;

  • (void)setName:(NSString *)name {
    if (_name != name) {
    [_name release];
    _name = [name copy];
    }
    }

  • (void)dealloc {
    self.name = nil;
    // 上邊這句相當(dāng)于下邊兩句
    [_name release];
    _name = nil;
    }
    復(fù)制代碼
    引用計(jì)數(shù)的使用

int main(int argc, const char * argv[]) {
@autoreleasepool {
// 1
Person *p = [[Person alloc] init];

    p.age = 20;
    
    // 0 (p指向的內(nèi)存已經(jīng)是壞內(nèi)存, 稱person對(duì)象為僵尸對(duì)象)
    // p稱為野指針, 野指針: 指向僵尸對(duì)象(壞內(nèi)存)的指針
    [p release];
    
    // p稱為空指針
    p = nil;
    
    p.age = 40;

// [0 setAge:40];

    // message sent to deallocated instance 0x100201950
    // 給空指針發(fā)消息不會(huì)報(bào)錯(cuò)
    [p release];
}
return 0;

}
復(fù)制代碼
堆和棧

import <Foundation/Foundation.h>

import "Car.h"

int main(int argc, const char * argv[]) {
@autoreleasepool {
int a = 10; // 棧

    int b = 20; // 棧
    
    // c : 棧
    // Car對(duì)象(計(jì)數(shù)器==1) : 堆
    Car *c = [[Car alloc] init];
}

// 當(dāng)autoreleasepool執(zhí)行完后后, 棧里面的變量a\b\c都會(huì)被回收
// 但是堆里面的Car對(duì)象還會(huì)留在內(nèi)存中, 因?yàn)樗怯?jì)數(shù)器依然是1

return 0;

}
復(fù)制代碼
看下面的程序,三次NSLog會(huì)輸出什么?為什么?

結(jié)果:3、2、1

NSMutableArray* ary = [[NSMutableArray array] retain];
NSString *str = [NSString stringWithFormat:@"test"]; // 1
[str retain]; // 2
[ary addObject:str]; // 3
NSLog(@"%d", [str retainCount]);
[str retain]; // 4
[str release]; // 3
[str release]; // 2
NSLog(@"%d", [str retainCount]);
[ary removeAllObjects]; // 1
NSLog(@"%d", [str retainCount]);
復(fù)制代碼
[NSArray arrayWithobject:]后需要對(duì)這個(gè)數(shù)組做釋放操作嗎?

答: 不需要,這個(gè)對(duì)象被放到自動(dòng)釋放池中

老版本的工程是可以轉(zhuǎn)換成使用ARC的工程,轉(zhuǎn)換規(guī)則包括:

去掉所有的retain,release,autorelease
把NSAutoRelease替換成@autoreleasepool{}塊
把a(bǔ)ssign的屬性變?yōu)閣eak使用ARC的一些強(qiáng)制規(guī)定
dealloc方法來(lái)管理一些資源,但不能用來(lái)釋放實(shí)例變量,也不能在dealloc方法里面去掉[super dealloc]方法,在ARC下父類的dealloc同樣由編譯器來(lái)自動(dòng)完成
Core Foundation類型的對(duì)象任然可以用CFRetain,CFRelease這些方法
不能在使用NSAllocateObject和NSDeallocateObject對(duì)象
不能在c結(jié)構(gòu)體中使用對(duì)象指針,如果有類似功能可以創(chuàng)建一個(gè)Objective-c類來(lái)管理這些對(duì)象
在id和void *之間沒有簡(jiǎn)便的轉(zhuǎn)換方法,同樣在Objective-c和core Foundation類型之間的轉(zhuǎn)換都需要使用編譯器制定的轉(zhuǎn)換函數(shù)
不能使用內(nèi)存存儲(chǔ)區(qū)(不能再使用NSZone)
不能以new為開頭給一個(gè)屬性命名
聲明outlet時(shí)一般應(yīng)當(dāng)使用weak,除了對(duì)StoryBoard,這樣nib中間的頂層對(duì)象要用strong
weak 相當(dāng)于老版本的assign,strong相當(dāng)于retain
文章來(lái)源于網(wǎng)絡(luò),如有侵權(quán),請(qǐng)聯(lián)系小編刪除,

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Swift1> Swift和OC的區(qū)別1.1> Swift沒有地址/指針的概念1.2> 泛型1.3> 類型嚴(yán)謹(jǐn) 對(duì)...
    cosWriter閱讀 11,658評(píng)論 1 32
  • 內(nèi)存管理 ARC處理原理 ARC是Objective-C編譯器的特性,而不是運(yùn)行時(shí)特性或者垃圾回收機(jī)制,ARC所做...
    b485c88ab697閱讀 11,347評(píng)論 3 47
  • 內(nèi)存管理 簡(jiǎn)述OC中內(nèi)存管理機(jī)制。與retain配對(duì)使用的方法是dealloc還是release,為什么?需要與a...
    丶逐漸閱讀 2,081評(píng)論 1 16
  • 1. 內(nèi)總管理原則(引用計(jì)數(shù)) IOS的對(duì)象都繼承于NSObject, 該對(duì)象有一個(gè)方法:retainCount...
    lilinjianshu閱讀 2,245評(píng)論 0 2
  • 內(nèi)存管理ARC處理原理ARC是Objective-C編譯器的特性,而不是運(yùn)行時(shí)特性或者垃圾回收機(jī)制,ARC所做的只...
    陽(yáng)明AI閱讀 426評(píng)論 0 3

友情鏈接更多精彩內(nèi)容