iOS经典面试题总结--内存管理
內(nèi)存管理
1.什么是ARC?
ARC是automatic reference counting自動(dòng)引用計(jì)數(shù),在程序編譯時(shí)自動(dòng)加入retain/release。在對(duì)象被創(chuàng)建時(shí)retain count+1,在對(duì)象被release時(shí)count-1,當(dāng)count=0時(shí),銷毀對(duì)象。程序中加入autoreleasepool對(duì)象會(huì)由系統(tǒng)自動(dòng)加上autorelease方法,如果該對(duì)象引用計(jì)數(shù)為0,則銷毀。那么ARC是為了解決MRC手動(dòng)管理內(nèi)存存在的一些而誕生的。
MRC下內(nèi)存管理的缺點(diǎn):
-
釋放一個(gè)堆內(nèi)存時(shí),首先要確定指向這個(gè)堆空間的指針都被release了。(避免提前釋放)
-
釋放指針指向的堆空間,首先要確定哪些指向同一個(gè)堆,這些指針只能釋放一次。(避免釋放多次,造成內(nèi)存泄露)
-
模塊化操作時(shí),對(duì)象可能被多個(gè)模塊創(chuàng)建和使用,不能確定最后由誰(shuí)釋放
-
多線程操作時(shí),不確定哪個(gè)線程最后使用完畢。
雖然ARC給我們編程帶來(lái)的很多好多,但也可能出現(xiàn)內(nèi)存泄露。如下面兩種情況:
-
循環(huán)參照:A有個(gè)屬性參照B,B有個(gè)屬性參照A,如果都是strong參照的話,兩個(gè)對(duì)象都無(wú)法釋放。
-
死循環(huán):如果有個(gè)ViewController中有無(wú)限循環(huán),也會(huì)導(dǎo)致即使ViewController對(duì)應(yīng)的view消失了,ViewController也不能釋放。
2.block一般用那個(gè)關(guān)鍵字修飾,為什么?
block一般使用copy關(guān)鍵之進(jìn)行修飾,block使用copy是從MRC遺留下來(lái)的“傳統(tǒng)”,在MRC中,方法內(nèi)容的block是在棧區(qū)的,使用copy可以把它放到堆區(qū)。但在ARC中寫不寫都行:編譯器自動(dòng)對(duì)block進(jìn)行了copy操作。
3.用@property聲明的NSString(或NSArray,NSDictionary)經(jīng)常使用copy關(guān)鍵字,為什么?如果改用strong關(guān)鍵字,可能造成什么問(wèn)題?
答:用@property聲明 NSString、NSArray、NSDictionary 經(jīng)常使用copy關(guān)鍵字,是因?yàn)樗麄冇袑?duì)應(yīng)的可變類型:NSMutableString、NSMutableArray、NSMutableDictionary,他們之間可能進(jìn)行賦值操作,為確保對(duì)象中的字符串值不會(huì)無(wú)意間變動(dòng),應(yīng)該在設(shè)置新屬性值時(shí)拷貝一份。
如果我們使用是strong,那么這個(gè)屬性就有可能指向一個(gè)可變對(duì)象,如果這個(gè)可變對(duì)象在外部被修改了,那么會(huì)影響該屬性。
copy此特質(zhì)所表達(dá)的所屬關(guān)系與strong類似。然而設(shè)置方法并不保留新值,而是將其“拷貝” (copy)。 當(dāng)屬性類型為NSString時(shí),經(jīng)常用此特質(zhì)來(lái)保護(hù)其封裝性,因?yàn)閭鬟f給設(shè)置方法的新值有可能指向一個(gè)NSMutableString類的實(shí)例。這個(gè)類是NSString的子類,表示一種可修改其值的字符串,此時(shí)若是不拷貝字符串,那么設(shè)置完屬性之后,字符串的值就可能會(huì)在對(duì)象不知情的情況下遭人更改。所以,這時(shí)就要拷貝一份“不可變” (immutable)的字符串,確保對(duì)象中的字符串值不會(huì)無(wú)意間變動(dòng)。只要實(shí)現(xiàn)屬性所用的對(duì)象是“可變的” (mutable),就應(yīng)該在設(shè)置新屬性值時(shí)拷貝一份。
4.runloop、autorelease pool以及線程之間的關(guān)系。
每個(gè)線程(包含主線程)都有一個(gè)Runloop。對(duì)于每一個(gè)Runloop,系統(tǒng)會(huì)隱式創(chuàng)建一個(gè)Autorelease pool,這樣所有的release pool會(huì)構(gòu)成一個(gè)像callstack一樣的一個(gè)棧式結(jié)構(gòu),在每一個(gè)Runloop結(jié)束時(shí),當(dāng)前棧頂?shù)腁utorelease pool會(huì)被銷毀,這樣這個(gè)pool里的每個(gè)Object會(huì)被release。
5.@property 的本質(zhì)是什么?ivar、getter、setter 是如何生成并添加到這個(gè)類中的。
“屬性”(property)有兩大概念:ivar(實(shí)例變量)、存取方法(access method=getter),即@property = ivar + getter + setter。
例如下面的這個(gè)類:
| 1 2 3 4 | @interface?WBTextView?:UITextView?? @property?(nonatomic,copy)NSString?*placehold;?? @property?(nonatomic,copy)UIColor?*placeholdColor;?? @end |
類完成屬性的定以后,編譯器會(huì)自動(dòng)編寫訪問(wèn)這些屬性的方法(自動(dòng)合成autosynthesis),上述代碼寫出來(lái)的類等效與下面的代碼:
| 1 2 3 4 5 6 | @interface?WBTextView?:UITextView?? -?(NSString?*)placehold;?? -(void)setPlacehold:(NSString?*)placehold;?? -(UIColor?*)placeholdColor;?? -(void)setPlaceholdColor:(UIColor?*)placeholdColor;?? @end |
詳細(xì)介紹見(jiàn):http://blog.csdn.net/jasonjwl/article/details/49427377
6.分別寫一個(gè)setter方法用于完成@property (nonatomic,retain)NSString *name和@property (nonatomic,copy) NSString *name
retain屬性的setter方法是保留新值并釋放舊值,然后更新實(shí)例變量,令其指向新值。順序很重要。假如還未保留新值就先把舊值釋放了,而且兩個(gè)值又指向同一個(gè)對(duì)象,先執(zhí)行的release操作就可能導(dǎo)致系統(tǒng)將此對(duì)象永久回收。
| 1 2 3 4 5 6 7 8 9 10 11 12 | -(void)setName:(NSString?*)name { ????[name?retain]; ????[_name?release]; ????_name?=?name; } -(void)setName:(NSString?*)name { ????? ????[_name?release]; ????_name?=?[name?copy]; } |
7.說(shuō)說(shuō)assign vs weak,_block vs _weak的區(qū)別
assign適用于基本數(shù)據(jù)類型,weak是適用于NSObject對(duì)象,并且是一個(gè)弱引用。
assign其實(shí)頁(yè)可以用來(lái)修飾對(duì)象,那么為什么不用它呢?因?yàn)楸籥ssign修飾的對(duì)象在釋放之后,指針的地址還是存在的,也就是說(shuō)指針并沒(méi)有被置為nil。如果在后續(xù)內(nèi)存分配中,剛才分到了這塊地址,程序就會(huì)崩潰掉。而weak修飾的對(duì)象在釋放之后,指針地址會(huì)被置為nil。
_block是用來(lái)修飾一個(gè)變量,這個(gè)變量就可以在block中被修改。
_block:使用_block修飾的變量在block代碼塊中會(huì)被retain(ARC下,MRC下不會(huì)retain)
_weak:使用_weak修飾的變量不會(huì)在block代碼塊中被retain
8.請(qǐng)說(shuō)出下面代碼是否有問(wèn)題,如果有問(wèn)題請(qǐng)修改?
| 1 2 3 4 5 6 | @autoreleasepool?{ ????????for?(int?i=0;?i[largeNumber;?i++)?{?(因識(shí)別問(wèn)題,該行代碼中尖括號(hào)改為方括號(hào)代替) ????????????Person?*per?=?[[Person?alloc]?init]; ????????????[per?autorelease]; ????????} ????} |
內(nèi)存管理的原則:如果對(duì)一個(gè)對(duì)象使用了alloc、copy、retain,那么你必須使用相應(yīng)的release或者autorelease。咋一看,這道題目有alloc,也有autorelease,兩者對(duì)應(yīng)起來(lái),應(yīng)該沒(méi)問(wèn)題。但autorelease雖然會(huì)使引用計(jì)數(shù)減一,但是它并不是立即減一,它的本質(zhì)功能只是把對(duì)象放到離他最近的自動(dòng)釋放池里。當(dāng)自動(dòng)釋放池銷毀了,才會(huì)向自動(dòng)釋放池中的每一個(gè)對(duì)象發(fā)送release消息。這道題的問(wèn)題就在autorelease。因?yàn)閘argeNumber是一個(gè)很大的數(shù),autorelease又不能使引用計(jì)數(shù)立即減一,所以在循環(huán)結(jié)束前會(huì)造成內(nèi)存溢出的問(wèn)題。
解決方案如下:
| 1 2 3 4 5 6 7 8 | @autoreleasepool?{ ????????for?(int?i=0;?i[100000;?i++)?{?(因識(shí)別問(wèn)題,該行代碼中尖括號(hào)改為方括號(hào)代替) ????????????@autoreleasepool?{ ????????????Person?*per?=?[[Person?alloc]?init]; ????????????[per?autorelease]; ????????} ??????} ????} |
在循環(huán)內(nèi)部再加一個(gè)自動(dòng)釋放池,這樣就能保證每創(chuàng)建一個(gè)對(duì)象就能及時(shí)釋放。
9.請(qǐng)問(wèn)下面代碼是否有問(wèn)題,如有問(wèn)題請(qǐng)修改?
| 1 2 3 4 5 6 7 8 9 | @autoreleasepool?{ ????????NSString?*str?=?[[NSString?alloc]?init]; ????????[str?retain]; ????????[str?retain]; ????????str?=?@"jxl"; ????????[str?release]; ????????[str?release]; ????????[str?release]; } |
這道題跟第8題一樣存在內(nèi)存泄露問(wèn)題,1.內(nèi)存泄露 2.指向常量區(qū)的對(duì)象不能release。
指針變量str原本指向一塊開(kāi)辟的堆區(qū)空間,但是經(jīng)過(guò)重新給str賦值,str的指向發(fā)生了變化,由原來(lái)指向堆區(qū)空間,到指向常量區(qū)。常量區(qū)的變量根本不需要釋放,這就導(dǎo)致了原來(lái)開(kāi)辟的堆區(qū)空間沒(méi)有釋放,照成內(nèi)存泄露。
10.什么情況下使用weak關(guān)鍵字,相比assign有什么不同?什么情況使用weak關(guān)鍵字?
-
在ARC中,在有可能出現(xiàn)循環(huán)引用的時(shí)候,往往要通過(guò)讓其中一端使用weak來(lái)解決。比如delegate代理
-
自身已經(jīng)對(duì)它進(jìn)行一次強(qiáng)引用,沒(méi)有必要再?gòu)?qiáng)引用一次,此時(shí)也會(huì)使用weak,自定義控件屬性一般也使用weak。
不同點(diǎn):
-
weak此特質(zhì)表明該屬性定義了一種“非擁有關(guān)系”。為這種屬性設(shè)置新值時(shí),設(shè)置方法既不保留新值,也不釋放舊值。此特性與assign一樣,然而在屬性所指的對(duì)象遭到推毀時(shí),屬性值也會(huì)清空。而assign的“設(shè)置方法”只會(huì)執(zhí)行針對(duì)“純量類型” (scalar type,例如 CGFloat 或 NSlnteger 等)的簡(jiǎn)單賦值操作。
-
assign可以用非OC對(duì)象,而weak必須用于OC對(duì)象。
11.內(nèi)存管理語(yǔ)義(assign、strong、weak等的區(qū)別)
-
assign “設(shè)置方法” 只會(huì)執(zhí)行針對(duì)“純量”的簡(jiǎn)單賦值操作。
-
strong ?此特質(zhì)表明該屬性定義了一種“擁有關(guān)系”。為這種屬性設(shè)置新值時(shí),設(shè)置方法會(huì)先保留新值,并釋放舊值,然后再將新值設(shè)置上去。
-
weak 此特質(zhì)表明該屬性定義了一種“非擁有關(guān)系”。為這種屬性設(shè)置新值時(shí),設(shè)置方法既不保留新值,也不釋放舊值。此特質(zhì)同assign類似,然而在屬性所指的對(duì)象遭到推毀時(shí),屬性值也會(huì)清空。
-
unsafe_unretained ?此特質(zhì)的語(yǔ)義和assign相同,但是它適用于“對(duì)象類型”,該特質(zhì)表達(dá)一種“非擁有關(guān)系”,當(dāng)目標(biāo)對(duì)象遭到推毀時(shí),屬性值不會(huì)自動(dòng)清空,這一點(diǎn)與weak有區(qū)別。
-
copy 此特質(zhì)所表達(dá)的所屬關(guān)系與strong類似。然而設(shè)置方法并不保留新值,而是設(shè)置方法并不保留新值,而是將其“拷貝”。當(dāng)屬性類型為NSString*時(shí),經(jīng)常用此特質(zhì)來(lái)保護(hù)其封裝性,因?yàn)閭鬟f給設(shè)置方法的新值有可能指向一個(gè)NSMutableString類的實(shí)例。這個(gè)類是NSString的子類,表示一種可以修改其值的字符串,此時(shí)若是不拷貝字符串,那么設(shè)置完屬性之后,字符串的值就可能會(huì)在對(duì)象不知情的情況下遭人更改。所以,這時(shí)就要拷貝一份“不可變”的字符串,確保對(duì)象中的字符串值不會(huì)無(wú)意間變動(dòng)。只要實(shí)現(xiàn)屬性所用的對(duì)象是“可變的”,就應(yīng)該在設(shè)置新屬性值時(shí)拷貝一份。
后續(xù)會(huì)繼續(xù)增加內(nèi)存管理方面的內(nèi)容以及多線程等內(nèi)容,持續(xù)更新中....,敬請(qǐng)期待!
參考:
-
招聘一個(gè)靠譜的iOS
-
《招聘一個(gè)靠譜的iOS》面試題參考答案(上)
-
Matt Galloway 《Effective Objective-C 2.0》
轉(zhuǎn)載于:https://www.cnblogs.com/it-k-50/p/5857897.html
總結(jié)
以上是生活随笔為你收集整理的iOS经典面试题总结--内存管理的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: js实现图片无缝连接
- 下一篇: JS操作DOM元素