內(nèi)存管理的一些概念
1.1 為什么要使用內(nèi)存管理?
嚴(yán)格的內(nèi)存管理悟衩,能夠是我們的應(yīng)用程在性能上有很大的提高
如果忽略內(nèi)存管理,可能導(dǎo)致應(yīng)用占用內(nèi)存過高虎囚,導(dǎo)致程序崩潰
1.2 OC的內(nèi)存管理主要有三種方式:
ARC(自動內(nèi)存計(jì)數(shù))
手動內(nèi)存計(jì)數(shù)
內(nèi)存池
1.3 OC中內(nèi)存管理的基本思想:
保證任何時(shí)候指向?qū)ο蟮闹羔槀€(gè)數(shù)和對象的引用計(jì)數(shù)相同验残,多一個(gè)指針指向這個(gè)對象這個(gè)對象的引用計(jì)數(shù)就加1,少一個(gè)指針指向這個(gè)對象這個(gè)對象的引用計(jì)數(shù)就減1泛释。沒有指針指向這個(gè)對象對象就被釋放了。
每個(gè)對象都有一個(gè)引用計(jì)數(shù)器温算,每個(gè)新對象的計(jì)數(shù)器是1怜校,當(dāng)對象的計(jì)數(shù)器減為0時(shí),就會被銷毀
通過retain可以讓對象的計(jì)數(shù)器+1注竿、release可以讓對象的計(jì)數(shù)器-1
還可以通過autorelease pool管理內(nèi)存
如果用ARC茄茁,編譯器會自動生成管理內(nèi)存的代碼
1.4 蘋果官方基礎(chǔ)內(nèi)存管理規(guī)則:
你擁有你創(chuàng)建的任何對象
你可以使用retain獲取一個(gè)對象的擁有權(quán)
當(dāng)你不再需要它,你必須放棄你擁有的對象的擁有權(quán)
你一定不能釋放不是你擁有的對象的擁有權(quán)
自動內(nèi)存管理
談?wù)勀銓?ARC 的認(rèn)識和理解巩割? ARC 是iOS 5推出的新功能裙顽。編譯器在代碼里適當(dāng)?shù)牡胤阶詣硬迦?retain / release 完成內(nèi)存管理(引用計(jì)數(shù))。
ARC機(jī)制中,系統(tǒng)判斷對象是否被銷毀的依據(jù)是什么?
指向?qū)ο蟮膹?qiáng)指針是否被銷毀
引用計(jì)數(shù)器
給對象發(fā)送一條retain消息宣谈,可以使引用計(jì)數(shù)器+1(retain方法返回對象本身)
給對象發(fā)送一條release消息愈犹,可以使引用計(jì)數(shù)器-1(注意release并不代表銷毀/回收對象,僅僅是計(jì)數(shù)器-1)
給對象發(fā)送retainCount消息闻丑,可以獲得當(dāng)前的引用計(jì)數(shù)值
簡單易用的標(biāo)題
2.1 自動釋放池底層怎么實(shí)現(xiàn)漩怎?
(以棧的方式實(shí)現(xiàn)的)(系統(tǒng)自動創(chuàng)建,系統(tǒng)自動釋放)棧里面的(先進(jìn)后出)
內(nèi)存里面有棧嗦嗡,棧里面有自動釋放池勋锤。
自動釋放池以棧的形式實(shí)現(xiàn):當(dāng)你創(chuàng)建一個(gè)新的自動釋放池時(shí),它將被添加到棧頂侥祭。當(dāng)一個(gè)對象收到發(fā)送autorelease消息時(shí),它被添加到當(dāng)前線程的處于棧頂?shù)淖詣俞尫懦刂腥矗?dāng)自動釋放池被回收時(shí),它們從棧中被刪除卑硫,并且會給池子里面所有的對象都會做一次release操作。
2.2 什么是自動釋放池?
答:自動釋放池是用來存儲多個(gè)對象類型的指針變量
2.3 自動釋放池對池內(nèi)對象的作用?
被存入到自動釋放池內(nèi)的對象蚕断,當(dāng)自動釋放池被銷毀時(shí)欢伏,會對池內(nèi)的對象全部做一次release操作
2.4 對象如何放入到自動釋放池中?
當(dāng)你確定要將對象放入到池中的時(shí)候,只需要調(diào)用對象的 autorelease 對象方法就可以把對象放入到自動釋放池中
2.5 多次調(diào)用對象的autorelease方法會導(dǎo)致什么問題?
答:多次將地址存到自動釋放池中,導(dǎo)致野指針異常
2.6 自動釋放池作用
將對象與自動釋放池建立關(guān)系亿乳,池子內(nèi)調(diào)用 autorelease 方法硝拧,在自動釋放池銷毀時(shí)銷毀對象径筏,延遲 release 銷毀時(shí)間
2.7 自動釋放池,什么時(shí)候創(chuàng)建?
程序剛啟動的時(shí)候障陶,也會創(chuàng)建一個(gè)自動釋放池
產(chǎn)生事件以后滋恬,運(yùn)行循環(huán)開始處理事件,就會創(chuàng)建自動釋放池
2.8 什么時(shí)候銷毀的?
程序運(yùn)行結(jié)束之前銷毀
事件處理結(jié)束以后抱究,會銷毀自動釋放池
還有在池子滿的時(shí)候恢氯,也會銷毀
2.9 自動釋放池使用注意:
不要把大量循環(huán)操作放在釋放池下,因?yàn)檫@會導(dǎo)致大量循環(huán)內(nèi)的對象沒有被回收鼓寺,這種情況下應(yīng)該手動寫 release 代碼勋拟。盡量避免對大內(nèi)存對象使用 autorelease ,否則會延遲大內(nèi)存的回收妈候。
2.10 autorelease的對象是在什么時(shí)候被release的敢靡?
答:autorelease實(shí)際上只是把對release的調(diào)用延遲了,對于每一個(gè)Autorelease苦银,系統(tǒng)只是把該Object放入了當(dāng)前的 Autoreleasepool中啸胧,當(dāng)該pool被釋放時(shí),該pool中的所有Object會被調(diào)用Release幔虏。對于每一個(gè)Runloop纺念,系統(tǒng)會隱式創(chuàng)建一個(gè)Autoreleasepool,這樣所有的releasepool會構(gòu)成一個(gè)象CallStack一樣的一個(gè)棧式結(jié)構(gòu)所计,在每一個(gè) Runloop結(jié)束時(shí)柠辞,當(dāng)前棧頂?shù)腁utoreleasepool會被銷毀,這樣這個(gè)pool里的每個(gè)Object(就是autorelease的對象)會被release主胧。那什么是一個(gè)Runloop呢叭首?一個(gè)UI事件,Timer call踪栋,delegate call焙格, 都會是一個(gè)新的Runloop。
2.11 If we don’t create any autorelease pool in our application then is there any autorelease pool already provided to us?
系統(tǒng)會默認(rèn)會不定時(shí)地創(chuàng)建和銷毀自動釋放池
2.12 When you will create an autorelease pool in your application?
當(dāng)不需要精確地控制對象的釋放時(shí)間時(shí)夷都,可以手動創(chuàng)建自動釋放池
@property內(nèi)存管理策略的選擇
讀寫屬性:readwrite 眷唉、readonly
setter語意:assign 、retain / copy
原子性(多線程管理):atomic 囤官、 nonatomic
強(qiáng)弱引用:strong 冬阳、 weak
3.1 讀寫屬性:
readwrite :同時(shí)生成 set 和 get 方法(默認(rèn))
readonly :只會生成 get 方法
3.2 控制set方法的內(nèi)存管理:
retain:release 舊值,retain 新值党饮。希望獲得源對象的所有權(quán)時(shí)肝陪,對其他 NSObject 和其子類(用于 OC 對象)
copy :release 舊值,copy 新值刑顺。希望獲得源對象的副本而不改變源對象內(nèi)容時(shí)(一般用于 NSString 氯窍,block )
assign :直接賦值饲常,不做任何內(nèi)存管理(默認(rèn)屬性),控制需不需生成 set 方法狼讨。對基礎(chǔ)數(shù)據(jù)類型 (NSInteger 贝淤,CGFloat )和C數(shù)據(jù)類型(int , float , double , char , 等等)
3.3 原子性(多線程管理):
atomic
默認(rèn)屬性,訪問方法都為原子型事務(wù)訪問政供。鎖被加到所屬對象實(shí)例級播聪,性能低。原子性就是說一個(gè)操作不可以中途被 cpu 暫停然后調(diào)度, 即不能被中斷, 要不就執(zhí)行完, 要不就不執(zhí)行. 如果一個(gè)操作是原子性的,那么在多線程環(huán)境下, 就不會出現(xiàn)變量被修改等奇怪的問題鲫骗。原子操作就是不可再分的操作犬耻,在多線程程序中原子操作是一個(gè)非常重要的概念,它常常用來實(shí)現(xiàn)一些同步機(jī)制执泰,同時(shí)也是一些常見的多線程 Bug 的源頭枕磁。當(dāng)然,原子性的變量在執(zhí)行效率上要低些术吝。
nonatomic
非原子性訪問计济。不加同步,盡量避免多線程搶奪同一塊資源排苍。是直接從內(nèi)存中取數(shù)值沦寂,因?yàn)樗菑膬?nèi)存中取得數(shù)據(jù),它并沒有一個(gè)加鎖的保護(hù)來用于cpu中的寄存器計(jì)算Value淘衙,它只是單純的從內(nèi)存地址中传藏,當(dāng)前的內(nèi)存存儲的數(shù)據(jù)結(jié)果來進(jìn)行使用。多線程并發(fā)訪問會提高性能彤守,但無法保證數(shù)據(jù)同步毯侦。盡量避免多線程搶奪同一塊資源,否則盡量將加鎖資源搶奪的業(yè)務(wù)邏輯交給服務(wù)器處理具垫,減少移動客戶端的壓力侈离。
當(dāng)有多個(gè)線程需要訪問到同一個(gè)數(shù)據(jù)時(shí),OC中筝蚕,我們可以使用 @synchronized (變量)來對該變量進(jìn)行加鎖(加鎖的目的常常是為了同步或保證原子操作)卦碾。
3.4 強(qiáng)指針(strong)、弱指針(weak)
strong
strong 系統(tǒng)一般不會自動釋放起宽,在 oc 中洲胖,對象默認(rèn)為強(qiáng)指針。作用域銷毀時(shí)銷毀引用坯沪。在實(shí)際開放中一般屬性對象一般 strong 來修飾(NSArray绿映,NSDictionary),在使用懶加載定義控件的時(shí)候屏箍,一般也用strong绘梦。
weak
weak 所引用對象的計(jì)數(shù)器不會加一,當(dāng)對象被釋放時(shí)指針會被自動賦值為 nil赴魁,系統(tǒng)會立刻釋放對象卸奉。
__unsafe_unretained 弱引用 當(dāng)對象被釋放時(shí)指針不會被自動賦值為 ni
在ARC時(shí)屬性的修飾符是可以用 assign 的(相當(dāng)于 __unsafe_unretained)
在ARC時(shí)屬性的修飾符是可以用 retain 的 (相當(dāng)于 __strong)
假定有N個(gè)指針指向同一個(gè)對象,如果至少有一個(gè)是強(qiáng)引用颖御,這個(gè)對象只要還在作用域內(nèi)就不會被釋放榄棵。相反,如果這N個(gè)指針都是弱引用潘拱,這個(gè)對象馬上就被釋放
在使用 sb 或者 xib 給控件拖線的時(shí)候疹鳄,為什么拖出來的先屬性都是用 weak 修飾呢?
由于在向 xib 或者 sb 里面添加控件的時(shí)候,添加的子視圖是添加到了跟視圖 View 上面芦岂,而 控制器 Controller 對其根視圖 View 默認(rèn)是強(qiáng)引用的瘪弓,當(dāng)我們的子控件添加到 view 上面的時(shí)候,self.view addSubView: 這個(gè)方法會對添加的控件進(jìn)行強(qiáng)引用禽最,如果在用 strong 對添加的子控件進(jìn)行修飾的話,相當(dāng)于有兩條強(qiáng)指針對子控件進(jìn)行強(qiáng)引用, 為了避免這種情況,所以用 weak 修飾腺怯。
注意:
(1)addSubView 默認(rèn)對其 subView 進(jìn)行了強(qiáng)引用
(2)在純手碼實(shí)現(xiàn)界面布局時(shí),如果通過懶加載處理界面控件川无,需要使用strong強(qiáng)指針
ARC管理內(nèi)存是用 assign 還是用 weak 呛占?
assign : 如果由于某些原因代理對象被釋放了,代理指針就變成了野指針懦趋。
weak : 如果由于某些原因代理對象被釋放了晾虑,代理指針就變成了空指針,更安全(weak 不能修飾基本數(shù)據(jù)類型仅叫,只能修飾對象)帜篇。
內(nèi)存分析
靜態(tài)分析(Analyze)
不運(yùn)行程序, 直接檢測代碼中是否有潛在的內(nèi)存問題(不一定百分百準(zhǔn)確, 僅僅是提供建議)
結(jié)合實(shí)際情況來分析, 是否真的有內(nèi)存問題
動態(tài)分析(Profile == Instruments)
運(yùn)行程序, 通過使用app,查看內(nèi)存的分配情況(Allocations):可以查看做出了某個(gè)操作后(比如點(diǎn)擊了某個(gè)按鈕\顯示了某個(gè)控制器)惑芭,內(nèi)存是否有暴增的情況(突然變化)
運(yùn)行程序, 通過使用app, 查看是否有內(nèi)存泄漏(Leaks):紅色區(qū)域代表內(nèi)存泄漏出現(xiàn)的地方
什么情況下會發(fā)生內(nèi)存泄漏和內(nèi)存溢出坠狡?
內(nèi)存泄漏:堆里不再使用的對象沒有被銷毀,依然占據(jù)著內(nèi)存遂跟。
內(nèi)存溢出:一次內(nèi)存泄露危害可以忽略逃沿,但內(nèi)存泄露多了,內(nèi)存遲早會被占光幻锁,最終會導(dǎo)致內(nèi)存溢出凯亮!當(dāng)程序在申請內(nèi)存時(shí),沒有足夠的內(nèi)存空間供其使用哄尔,出現(xiàn)out of memory假消;比如數(shù)據(jù)長度比較小的數(shù)據(jù)類型 存儲了數(shù)據(jù)長度比較大的數(shù)據(jù)。
關(guān)于圖片占用內(nèi)存管理
4.1 圖片加載占用內(nèi)存對比
使用 imageName: 加載圖片:
加載到內(nèi)存當(dāng)中后岭接,占據(jù)內(nèi)存空間較大
相同的圖片富拗,圖片不會重復(fù)加載
加載內(nèi)存當(dāng)中之后臼予,會一直停留在內(nèi)存當(dāng)中,不會隨著對象銷毀而銷毀
加載進(jìn)去圖片之后啃沪,占用的內(nèi)存歸系統(tǒng)管理粘拾,我們無法管理
使用 imageWithContentsOfFile: 加載圖片
加載到內(nèi)存當(dāng)中后,占據(jù)內(nèi)存空間較小
相同的圖片會被重復(fù)加載內(nèi)存當(dāng)中
對象銷毀的時(shí)候,加載到內(nèi)存中圖片會隨著一起銷毀
結(jié)論:
圖片較小,并且使用頻繁创千,使用 imageName: 來加載(按鈕圖標(biāo)/主頁里面圖片)
圖片較大缰雇,并且使用較少,使用 imageWithContentsOfFile: 來加載(版本新特性/相冊)
4.2 圖片在沙盒中的存在形式
部署版本在>=iOS8的時(shí)候追驴,打包的資源包中的圖片會被放到Assets.car械哟。圖片有被壓縮;
部署版本在<iOS8的時(shí)候殿雪,打包的資源包中的圖片會被放在MainBudnle里面暇咆。圖片沒有被壓縮
沒有放在Images.xcassets里面的所有圖片會直接暴露在沙盒的資源包(main Bundle), 不會壓縮到Assets.car文件,會被放到MainBudnle里面丙曙。圖片沒有被壓縮
結(jié)論:
小圖片\使用頻率比較高的圖片放在Images.xcassets里面
大圖片\使用頻率比較低的圖片(一次性的圖片, 比如版本新特性的圖片)不要放在Images.xcassets里面
內(nèi)存管理問題
5.1 單個(gè)對象內(nèi)存管理的問題
關(guān)于內(nèi)存我們主要研究的問題是什么? 野指針:對象的retainCount已經(jīng)為0,保存了對象指針地址的變量就是野指針糯崎。使用野指針調(diào)用對象的方法,會導(dǎo)致野指針異常,導(dǎo)致程序直接崩潰
內(nèi)存泄露:已經(jīng)不在使用的對象,沒有正確的釋放掉,一直駐留在內(nèi)存中,我們就說是內(nèi)存泄漏
僵尸對象? retainCount = 0的對象被稱之為僵尸對象,也就是不能夠在訪問的對象
是什么問題導(dǎo)致,訪問僵尸對象,時(shí)而正確時(shí)而錯(cuò)誤?
如何開始xcode的時(shí)時(shí)檢測僵尸對象功能?
當(dāng)對象的retainCount = 0 時(shí) 能否調(diào)用 retain方法使對象復(fù)活? 已經(jīng)被釋放的對象是無法在復(fù)活的
如何防止出現(xiàn)野指針操作? 通常在調(diào)用完release方法后,會把保存了對象指針地址的變量清空河泳,賦值為nil 在oc中沒有空指針異常,所以使用[nil retain]調(diào)用方法不會導(dǎo)致異常的發(fā)生
內(nèi)存泄漏有幾種情況?
沒有配對釋放沃呢,不符合內(nèi)存管理原則
對象提前賦值為nil或者清空,導(dǎo)致release方法沒有起作用
5.2 多個(gè)對象內(nèi)存管理的問題
對象與對象之間存在幾種關(guān)系?
繼承關(guān)系
組合關(guān)系
對象作為方法參數(shù)傳遞
對象的組合關(guān)系中,如何確保作為成員變量的對象,不會被提前釋放? 重寫set方法拆挥,在set方法中薄霜,retain該對像,使其retainCount值增加 1
組合關(guān)系導(dǎo)致內(nèi)存泄漏的原因是什么? 在set方法中纸兔,retain了該對象惰瓜,但是并沒有配對釋放
作為成員變量的對象,應(yīng)該在那里配對釋放? 在dealloc函數(shù)中釋放
內(nèi)存相關(guān)的一些數(shù)據(jù)結(jié)構(gòu)的對比
6.1 簡述內(nèi)存分區(qū)情況
代碼區(qū):存放函數(shù)二進(jìn)制代碼
數(shù)據(jù)區(qū):系統(tǒng)運(yùn)行時(shí)申請內(nèi)存并初始化,系統(tǒng)退出時(shí)由系統(tǒng)釋放汉矿。存放全局變量崎坊、靜態(tài)變量、常量
堆區(qū):通過malloc等函數(shù)或new等操作符動態(tài)申請得到洲拇,需程序員手動申請和釋放
棧區(qū):函數(shù)模塊內(nèi)申請奈揍,函數(shù)結(jié)束時(shí)由系統(tǒng)自動釋放。存放局部變量赋续、函數(shù)參數(shù)
6.2 手機(jī)的存儲空間分為內(nèi)存(RAM)和閃存(Flash)兩種
內(nèi)存一般較心泻病:1G、2G纽乱、3G蛾绎、4G。閃存空間相對較大16G、32G租冠、64G鹏倘;
內(nèi)存的讀寫速度較快、閃存的讀寫速度相對較慢顽爹;
內(nèi)存里的東西掉電后全部丟失第股、閃存里的東西掉電也不丟;
內(nèi)存相當(dāng)于電腦的內(nèi)存條话原、閃存相當(dāng)于電腦的硬盤;
6.3 堆和棧的區(qū)別诲锹?
管理方式:
堆釋放工作由程序員控制繁仁,容易產(chǎn)生memory leak;
棧是由編譯器自動管理归园,無需我們手工控制黄虱。
申請大小:
堆:堆是向高地址擴(kuò)展的數(shù)據(jù)結(jié)構(gòu)庸诱,是不連續(xù)的內(nèi)存區(qū)域捻浦。這是由于系統(tǒng)是用鏈表來存儲的空閑內(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(也有的說是1M褥影,總之是一個(gè)編譯時(shí)就確定的常數(shù)),如果申請的空間超過棧的剩余空間時(shí)咏雌,將提示overflow凡怎。因此绩衷,能從棧獲得的空間較小闻伶。
碎片問題:
堆:頻繁的new/delete勢必會造成內(nèi)存空間的不連續(xù)专控,從而造成大量的碎片亮航,使程序效率降低羽德。
棧:則不會存在這個(gè)問題船响,因?yàn)闂J窍冗M(jìn)后出的隊(duì)列材原,他們是如此的一一對應(yīng)袍冷,以至于永遠(yuǎn)都不可能有一個(gè)內(nèi)存塊從棧中間彈出
分配方式:
堆都是動態(tài)分配的,沒有靜態(tài)分配的堆坛缕。
棧有2種分配方式:靜態(tài)分配和動態(tài)分配墓猎。靜態(tài)分配是編譯器完成的,比如局部變量的分配赚楚。動態(tài)分配由alloc函數(shù)進(jìn)行分配毙沾,但是棧的動態(tài)分配和堆是不同的,他的動態(tài)分配是由編譯器進(jìn)行釋放宠页,無需我們手工實(shí)現(xiàn)左胞。
分配效率:
棧:是機(jī)器系統(tǒng)提供的數(shù)據(jù)結(jié)構(gòu),計(jì)算機(jī)會在底層對棧提供支持:分配專門的寄存器存放棧的地址举户,壓棧出棧都有專門的指令執(zhí)行烤宙,這就決定了棧的效率比較高。
堆:則是C/C++函數(shù)庫提供的俭嘁,它的機(jī)制是很復(fù)雜的躺枕。
每個(gè)App有個(gè)內(nèi)存空間,假定是4G供填,分為堆和棧兩大部分拐云。一般來說每個(gè)進(jìn)程有一個(gè)堆(這個(gè)進(jìn)程的所有線程共用這個(gè)堆),進(jìn)程中的線程有自己棧近她。
通過alloc叉瘩、new或malloc獲得的內(nèi)存在堆中分配,堆中的內(nèi)存需要寫相應(yīng)的代碼釋放粘捎。如果進(jìn)程結(jié)束了在堆中分配的內(nèi)存會自動釋放房揭。
局部變量、函數(shù)參數(shù)是在椛味耍空間中分配捅暴,如果函數(shù)返回這個(gè)函數(shù)中的局部變量、參數(shù)所占的內(nèi)存系統(tǒng)自動釋放(回收)咧纠。
程序在編譯期對變量和函數(shù)分配內(nèi)存都在棧上進(jìn)行蓬痒,且程序運(yùn)行過程中函數(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)兆沙。如果需要快速訪問數(shù)據(jù),很少或不插入和刪除元素莉掂,就應(yīng)該用數(shù)組葛圃;相反, 如果需要經(jīng)常插入和刪除元素就需要用鏈表數(shù)據(jù)結(jié)構(gòu)憎妙。
從邏輯結(jié)構(gòu)來看
數(shù)組必須事先定義固定的長度(元素個(gè)數(shù))库正,不能適應(yīng)數(shù)據(jù)動態(tài)地增減的情況。當(dāng)數(shù)據(jù)增加時(shí)厘唾,可能超出原先定義的元素個(gè)數(shù)褥符;當(dāng)數(shù)據(jù)減少時(shí),造成內(nèi)存浪費(fèi)阅嘶;數(shù)組可以根據(jù)下標(biāo)直接存取。
鏈表動態(tài)地進(jìn)行存儲分配载迄,可以適應(yīng)數(shù)據(jù)動態(tài)地增減的情況讯柔,且可以方便地插入、刪除數(shù)據(jù)項(xiàng)护昧。(數(shù)組中插入魂迄、刪除數(shù)據(jù)項(xiàng)時(shí),需要移動其它數(shù)據(jù)項(xiàng)惋耙,非常繁瑣)鏈表必須根據(jù)next指針找到下一個(gè)元素
從內(nèi)存存儲來看
數(shù)組從棧中分配空間捣炬,對于程序員方便快速,但是自由度小
鏈表從堆中分配空間, 自由度大但是申請管理比較麻煩
面試題
如何讓程序盡量減少內(nèi)存泄漏
非ARC
Foundation 對象( OC 對象) : 只要方法中包含了 alloc\new\copy\mutableCopy\retain 等關(guān)鍵字绽榛,那么這些方法產(chǎn)生的對象, 就必須在不再使用的時(shí)候調(diào)用1次 release 或者1次 autorelease湿酸。
CoreFoundation 對象( C 對象) : 只要函數(shù)中包含了 create\new\copy\retain 等關(guān)鍵字, 那么這些方法產(chǎn)生的對象, 就必須在不再使用的時(shí)候調(diào)用1次 CFRelease 或者其他 release 函數(shù)。
ARC(只自動管理OC對象, 不會自動管理C語言對象)
CoreFoundation 對象( C 對象) : 只要函數(shù)中包含了 create\new\copy\retain 等關(guān)鍵字, 那么這些方法產(chǎn)生的對象, 就必須在不再使用的時(shí)候調(diào)用1次 CFRelease 或者其他 release 函數(shù)灭美。
block的注意
// block的內(nèi)存默認(rèn)在棧里面(系統(tǒng)自動管理)
void (^test)() = ^{
};
// 如果對block進(jìn)行了Copy操作, block的內(nèi)存會遷移到堆里面(需要通過代碼管理內(nèi)存)
Block_copy(test);
// 在不需要使用block的時(shí)候, 應(yīng)該做1次release操作
Block_release(test);
[test release];
野指針舉例
建了個(gè)視圖控制器(ARC時(shí))某個(gè)函數(shù)里寫了如下代碼推溃。當(dāng)這個(gè)函數(shù)返回時(shí)因?yàn)闆]有指針指向b所以b會被釋放、但是b.view不會被釋放届腐。如果在b里有需要操作b的地方(比如代理的方法)铁坎,就會產(chǎn)生野指針(提前釋放)
B *b = [[B alloc]init];
[self.view addSubview:b.view];
set方法
在對象的組合關(guān)系中,導(dǎo)致內(nèi)存泄漏有幾種情況? 1.set方法中沒有retain對象 2.沒有release掉舊的對象 3.沒有判斷向set方法中傳入的是否是同一個(gè)對象
該如何正確的重寫set方法? 1.先判斷是否是同一個(gè)對象 2.release一次舊的對象 3.retain新的對象
寫一個(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;
}
引用計(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對象為僵尸對象) // p稱為野指針, 野指針: 指向僵尸對象(壞內(nèi)存)的指針 [p release]; // p稱為空指針 p = nil; p.age = 40;
// [0 setAge:40];
// message sent to deallocated instance 0x100201950
// 給空指針發(fā)消息不會報(bào)錯(cuò)
[p release];
}
return 0;
}
堆和棧
import <Foundation/Foundation.h>
import "Car.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
int a = 10; // 棧
int b = 20; // 棧
// c : 棧
// Car對象(計(jì)數(shù)器==1) : 堆
Car *c = [[Car alloc] init];
}
// 當(dāng)autoreleasepool執(zhí)行完后后, 棧里面的變量a\b\c都會被回收
// 但是堆里面的Car對象還會留在內(nèi)存中, 因?yàn)樗怯?jì)數(shù)器依然是1
return 0;
}
看下面的程序,三次NSLog會輸出什么硬萍?為什么?
結(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]);
[NSArray arrayWithobject:]后需要對這個(gè)數(shù)組做釋放操作嗎?
答:不需要,這個(gè)對象被放到自動釋放池中
老版本的工程是可以轉(zhuǎn)換成使用ARC的工程,轉(zhuǎn)換規(guī)則包括:
去掉所有的retain寒砖,release赐劣,autorelease
把NSAutoRelease替換成@autoreleasepool{}塊
把a(bǔ)ssign的屬性變?yōu)閣eak使用ARC的一些強(qiáng)制規(guī)定
dealloc方法來管理一些資源,但不能用來釋放實(shí)例變量哩都,也不能在dealloc方法里面去掉[super dealloc]方法魁兼,在ARC下父類的dealloc同樣由編譯器來自動完成
Core Foundation類型的對象任然可以用CFRetain,CFRelease這些方法
不能在使用NSAllocateObject和NSDeallocateObject對象
不能在c結(jié)構(gòu)體中使用對象指針漠嵌,如果有類似功能可以創(chuàng)建一個(gè)Objective-c類來管理這些對象
在id和void *之間沒有簡便的轉(zhuǎn)換方法咐汞,同樣在Objective-c和core Foundation類型之間的轉(zhuǎn)換都需要使用編譯器制定的轉(zhuǎn)換函數(shù)
不能使用內(nèi)存存儲區(qū)(不能再使用NSZone)
不能以new為開頭給一個(gè)屬性命名
聲明outlet時(shí)一般應(yīng)當(dāng)使用weak,除了對StoryBoard儒鹿,這樣nib中間的頂層對象要用strong
weak 相當(dāng)于老版本的assign化撕,strong相當(dāng)于retain