1. 程式人生 > >iOS面試之——記憶體管理

iOS面試之——記憶體管理

記憶體管理

1.什麼是ARC?

ARC是automatic reference counting自動引用計數,在程式編譯時自動加入retain/release。在物件被建立時retain count+1,在物件被release時count-1,當count=0時,銷燬物件。程式中加入autoreleasepool物件會由系統自動加上autorelease方法,如果該物件引用計數為0,則銷燬。那麼ARC是為了解決MRC手動管理記憶體存在的一些而誕生的。
MRC下記憶體管理的缺點:

釋放一個堆記憶體時,首先要確定指向這個堆空間的指標都被release了。(避擴音前釋放)

釋放指標指向的堆空間,首先要確定哪些指向同一個堆,這些指標只能釋放一次。(避免釋放多次,造成記憶體洩露)

模組化操作時,物件可能被多個模組建立和使用,不能確定最後由誰釋放

多執行緒操作時,不確定哪個執行緒最後使用完畢。
雖然ARC給我們程式設計帶來的很多好多,但也可能出現記憶體洩露。如下面兩種情況:

迴圈參照:A有個屬性參照B,B有個屬性參照A,如果都是strong參照的話,兩個物件都無法釋放。

死迴圈:如果有個ViewController中有無限迴圈,也會導致即使ViewController對應的view消失了,ViewController也不能釋放。
2.block一般用那個關鍵字修飾,為什麼?

block一般使用copy關鍵之進行修飾,block使用copy是從MRC遺留下來的“傳統”,在MRC中,方法內容的block是在棧區的,使用copy可以把它放到堆區。但在ARC中寫不寫都行:編譯器自動對block進行了copy操作。
3.用@property宣告的NSString(或NSArray,NSDictionary)經常使用copy關鍵字,為什麼?如果改用strong關鍵字,可能造成什麼問題?

答:用@property宣告 NSString、NSArray、NSDictionary 經常使用copy關鍵字,是因為他們有對應的可變型別:NSMutableString、NSMutableArray、NSMutableDictionary,他們之間可能進行賦值操作,為確保物件中的字串值不會無意間變動,應該在設定新屬性值時拷貝一份。

如果我們使用是strong,那麼這個屬性就有可能指向一個可變物件,如果這個可變物件在外部被修改了,那麼會影響該屬性。

copy此特質所表達的所屬關係與strong類似。然而設定方法並不保留新值,而是將其“拷貝” (copy)。 當屬性型別為NSString時,經常用此特質來保護其封裝性,因為傳遞給設定方法的新值有可能指向一個NSMutableString類的例項。這個類是NSString的子類,表示一種可修改其值的字串,此時若是不拷貝字串,那麼設定完屬性之後,字串的值就可能會在物件不知情的情況下遭人更改。所以,這時就要拷貝一份“不可變” (immutable)的字串,確保物件中的字串值不會無意間變動。只要實現屬性所用的物件是“可變的” (mutable),就應該在設定新屬性值時拷貝一份。
4.runloop、autorelease pool以及執行緒之間的關係。

每個執行緒(包含主執行緒)都有一個Runloop。對於每一個Runloop,系統會隱式建立一個Autorelease pool,這樣所有的release pool會構成一個像callstack一樣的一個棧式結構,在每一個Runloop結束時,當前棧頂的Autorelease pool會被銷燬,這樣這個pool裡的每個Object會被release。
[email protected] 的本質是什麼?ivar、getter、setter 是如何生成並新增到這個類中的。

“屬性”(property)有兩大概念:ivar(例項變數)、存取方法(access method=getter),即@property = ivar + getter + setter。

例如下面的這個類:

@interface WBTextView :UITextView

@property (nonatomic,copy)NSString *placehold;

@property (nonatomic,copy)UIColor *placeholdColor;

@end

類完成屬性的定以後,編譯器會自動編寫訪問這些屬性的方法(自動合成autosynthesis),上述程式碼寫出來的類等效與下面的程式碼:

@interface WBTextView :UITextView

  • (NSString *)placehold;

-(void)setPlacehold:(NSString *)placehold;

-(UIColor *)placeholdColor;

-(void)setPlaceholdColor:(UIColor *)placeholdColor;

@end

詳細介紹見:http://blog.csdn.net/jasonjwl/article/details/49427377
6.分別寫一個setter方法用於完成

@property (nonatomic,retain)NSString *name和

@property (nonatomic,copy) NSString *name

retain屬性的setter方法是保留新值並釋放舊值,然後更新例項變數,令其指向新值。順序很重要。假如還未保留新值就先把舊值釋放了,而且兩個值又指向同一個物件,先執行的release操作就可能導致系統將此物件永久回收。

-(void)setName:(NSString *)name

{

[name retain];

[_name release];

_name = name;

}
-(void)setName:(NSString *)name

{

[_name release];

_name = [name copy];

}
7.說說assign vs weak,_block vs _weak的區別

assign適用於基本資料型別,weak是適用於NSObject物件,並且是一個弱引用。

assign其實頁可以用來修飾物件,那麼為什麼不用它呢?因為被assign修飾的物件在釋放之後,指標的地址還是存在的,也就是說指標並沒有被置為nil。如果在後續記憶體分配中,剛才分到了這塊地址,程式就會崩潰掉。而weak修飾的物件在釋放之後,指標地址會被置為nil。

_block是用來修飾一個變數,這個變數就可以在block中被修改。

_block:使用_block修飾的變數在block程式碼塊中會被retain(ARC下,MRC下不會retain)

_weak:使用_weak修飾的變數不會在block程式碼塊中被retain
8.請說出下面程式碼是否有問題,如果有問題請修改?

@autoreleasepool {

for (int i=0; i[largeNumber; i++) { (因識別問題,該行程式碼中尖括號改為方括號代替)

Person *per = [[Person alloc] init];

[per autorelease];

} }
記憶體管理的原則:如果對一個物件使用了alloc、copy、retain,那麼你必須使用相應的release或者autorelease。咋一看,這道題目有alloc,也有autorelease,兩者對應起來,應該沒問題。但autorelease雖然會使引用計數減一,但是它並不是立即減一,它的本質功能只是把物件放到離他最近的自動釋放池裡。當自動釋放池銷燬了,才會向自動釋放池中的每一個物件傳送release訊息。這道題的問題就在autorelease。因為largeNumber是一個很大的數,autorelease又不能使引用計數立即減一,所以在迴圈結束前會造成記憶體溢位的問題。

解決方案如下:

@autoreleasepool {

for (int i=0; i[100000; i++) { (因識別問題,該行程式碼中尖括號改為方括號代替)

@autoreleasepool {

Person *per = [[Person alloc] init];

[per autorelease];

}}}
在迴圈內部再加一個自動釋放池,這樣就能保證每建立一個物件就能及時釋放。

9.請問下面程式碼是否有問題,如有問題請修改?

@autoreleasepool {

NSString *str = [[NSString alloc] init];

[str retain];

[str retain];

str = @”jxl”;

[str release];

[str release];

[str release];

}
這道題跟第8題一樣存在記憶體洩露問題,1.記憶體洩露 2.指向常量區的物件不能release。

指標變數str原本指向一塊開闢的堆區空間,但是經過重新給str賦值,str的指向發生了變化,由原來指向堆區空間,到指向常量區。常量區的變數根本不需要釋放,這就導致了原來開闢的堆區空間沒有釋放,照成記憶體洩露。
10.什麼情況下使用weak關鍵字,相比assign有什麼不同?什麼情況使用weak關鍵字?

在ARC中,在有可能出現迴圈引用的時候,往往要通過讓其中一端使用weak來解決。比如delegate代理

自身已經對它進行一次強引用,沒有必要再強引用一次,此時也會使用weak,自定義控制元件屬性一般也使用weak。
不同點:

weak此特質表明該屬性定義了一種“非擁有關係”。為這種屬性設定新值時,設定方法既不保留新值,也不釋放舊值。此特性與assign一樣,然而在屬性所指的物件遭到推毀時,屬性值也會清空。而assign的“設定方法”只會執行鍼對“純量型別” (scalar type,例如 CGFloat 或 NSlnteger 等)的簡單賦值操作。

assign可以用非OC物件,而weak必須用於OC物件。
11.記憶體管理語義(assign、strong、weak等的區別)

assign “設定方法” 只會執行鍼對“純量”的簡單賦值操作。

strong 此特質表明該屬性定義了一種“擁有關係”。為這種屬性設定新值時,設定方法會先保留新值,並釋放舊值,然後再將新值設定上去。

weak 此特質表明該屬性定義了一種“非擁有關係”。為這種屬性設定新值時,設定方法既不保留新值,也不釋放舊值。此特質同assign類似,然而在屬性所指的物件遭到推毀時,屬性值也會清空。

unsafe_unretained 此特質的語義和assign相同,但是它適用於“物件型別”,該特質表達一種“非擁有關係”,當目標物件遭到推毀時,屬性值不會自動清空,這一點與weak有區別。

copy 此特質所表達的所屬關係與strong類似。然而設定方法並不保留新值,而是設定方法並不保留新值,而是將其“拷貝”。當屬性型別為NSString*時,經常用此特質來保護其封裝性,因為傳遞給設定方法的新值有可能指向一個NSMutableString類的例項。這個類是NSString的子類,表示一種可以修改其值的字串,此時若是不拷貝字串,那麼設定完屬性之後,字串的值就可能會在物件不知情的情況下遭人更改。所以,這時就要拷貝一份“不可變”的字串,確保物件中的字串值不會無意間變動。只要實現屬性所用的物件是“可變的”,就應該在設定新屬性值時拷貝一份。