爲何要使用內存管理?程序員
OC的內存管理主要有三種方式:面試
OC中內存管理的基本思想:
保證任什麼時候候指向對象的指針個數和對象的引用計數相同,多一個指針指向這個對象這個對象的引用計數就加1,少一個指針指向這個對象這個對象的引用計數就減1。沒有指針指向這個對象對象就被釋放了。數組
蘋果官方基礎內存管理規則:xcode
談談你對 ARC
的認識和理解? ARC
是iOS 5推出的新功能。編譯器在代碼裏適當的地方自動插入 retain
/ release
完成內存管理(引用計數)。安全
ARC機制中,系統判斷對象是否被銷燬的依據是什麼?
指向對象的強指針是否被銷燬bash
自動釋放池底層怎麼實現?
(以棧的方式實現的)(系統自動建立,系統自動釋放)棧裏面的(先進後出)
內存裏面有棧,棧裏面有自動釋放池。
自動釋放池以棧的形式實現:當你建立一個新的自動釋放池時,它將被添加到棧頂。當一個對象收到發送autorelease消息時,它被添加到當前線程的處於棧頂的自動釋放池中,當自動釋放池被回收時,它們從棧中被刪除,而且會給池子裏面全部的對象都會作一次release操做。服務器
什麼是自動釋放池?
答:自動釋放池是用來存儲多個對象類型的指針變量數據結構
自動釋放池對池內對象的做用? 被存入到自動釋放池內的對象,當自動釋放池被銷燬時,會對池內的對象所有作一次release操做多線程
對象如何放入到自動釋放池中? 當你肯定要將對象放入到池中的時候,只須要調用對象的 autorelease
對象方法就能夠把對象放入到自動釋放池中併發
屢次調用對象的autorelease方法會致使什麼問題?
答:屢次將地址存到自動釋放池中,致使野指針異常
自動釋放池做用
將對象與自動釋放池創建關係,池子內調用 autorelease
方法,在自動釋放池銷燬時銷燬對象,延遲 release
銷燬時間
自動釋放池,何時建立?
何時銷燬的?
自動釋放池使用注意:
不要把大量循環操做放在釋放池下,由於這會致使大量循環內的對象沒有被回收,這種狀況下應該手動寫 release
代碼。儘可能避免對大內存對象使用 autorelease
,不然會延遲大內存的回收。
autorelease的對象是在何時被release的?
答:autorelease實際上只是把對release的調用延遲了,對於每個Autorelease,系統只是把該Object放入了當前的 Autoreleasepool中,當該pool被釋放時,該pool中的全部Object會被調用Release。對於每個Runloop,系統會隱式建立一個Autoreleasepool,這樣全部的releasepool會構成一個象CallStack同樣的一個棧式結構,在每個 Runloop結束時,當前棧頂的Autoreleasepool會被銷燬,這樣這個pool裏的每一個Object(就是autorelease的對象)會被release。那什麼是一個Runloop呢?一個UI事件,Timer call,delegate call, 都會是一個新的Runloop。
If we don’t create any autorelease pool in our application then is there any autorelease pool already provided to us?
系統會默認會不定時地建立和銷燬自動釋放池
When you will create an autorelease pool in your application?
當不須要精確地控制對象的釋放時間時,能夠手動建立自動釋放池
讀寫屬性:readwrite
、readonly
setter語意:assign
、retain
/ copy
原子性(多線程管理):atomic
、 nonatomic
強弱引用:strong
、 weak
讀寫屬性:
readwrite
:同時生成 set
和 get
方法(默認)
readonly
:只會生成 get
方法
控制set方法的內存管理:
retain
:release
舊值,retain
新值。但願得到源對象的全部權時,對其餘 NSObject
和其子類(用於 OC
對象)
copy
:release
舊值,copy
新值。但願得到源對象的副本而不改變源對象內容時(通常用於 NSString
,block
)
assign
:直接賦值,不作任何內存管理(默認屬性),控制需不需生成 set
方法。對基礎數據類型 (NSInteger
,CGFloat
)和C數據類型(int
, float
, double
, char
, 等等)
原子性(多線程管理):
@synchronized
(變量)來對該變量進行加鎖(加鎖的目的經常是爲了同步或保證原子操做)。強指針(strong)、弱指針(weak)
strong
strong
系統通常不會自動釋放,在 oc
中,對象默認爲強指針。做用域銷燬時銷燬引用。在實際開放中通常屬性對象通常 strong
來修飾(NSArray
,NSDictionary
),在使用懶加載定義控件的時候,通常也用strong。weak
weak
所引用對象的計數器不會加一,當對象被釋放時指針會被自動賦值爲 nil
,系統會馬上釋放對象。__unsafe_unretained
弱引用 當對象被釋放時指針不會被自動賦值爲 ni
assign
的(至關於 __unsafe_unretained
)retain
的 (至關於 __strong
)sb
或者 xib
給控件拖線的時候,爲何拖出來的先屬性都是用 weak 修飾呢?xib
或者 sb
裏面添加控件的時候,添加的子視圖是添加到了跟視圖 View
上面,而 控制器 Controller
對其根視圖 View
默認是強引用的,當咱們的子控件添加到 view
上面的時候,self.view addSubView:
這個方法會對添加的控件進行強引用,若是在用 strong
對添加的子控件進行修飾的話,至關於有兩條強指針對子控件進行強引用, 爲了不這種狀況,因此用 weak
修飾。ARC管理內存是用 assign
仍是用 weak
?
assign
: 若是因爲某些緣由代理對象被釋放了,代理指針就變成了野指針。
weak
: 若是因爲某些緣由代理對象被釋放了,代理指針就變成了空指針,更安全(weak
不能修飾基本數據類型,只能修飾對象)。
內存泄漏:堆裏再也不使用的對象沒有被銷燬,依然佔據着內存。
內存溢出:一次內存泄露危害能夠忽略,但內存泄露多了,內存早晚會被佔光,最終會致使內存溢出!當程序在申請內存時,沒有足夠的內存空間供其使用,出現out of memory;好比數據長度比較小的數據類型 存儲了數據長度比較大的數據。
圖片加載佔用內存對比
imageName:
加載圖片:
imageWithContentsOfFile:
加載圖片
imageName:
來加載(按鈕圖標/主頁裏面圖片)imageWithContentsOfFile:
來加載(版本新特性/相冊)圖片在沙盒中的存在形式
簡述內存分區狀況
手機的存儲空間分爲內存(RAM)和閃存(Flash)兩種
堆和棧的區別?
隊列和棧有什麼區別:
隊列和棧是兩種不一樣的數據容器。從」數據結構」的角度看,它們都是線性結構,即數據元素之間的關係相同。
隊列是一種先進先出的數據結構,它在兩端進行操做,一端進行入隊列操做,一端進行出列隊操做。
棧是一種先進後出的數據結構,它只能在棧頂進行操做,入棧和出棧都在棧頂操做。
鏈表和數組的區別在哪裏?
兩者都屬於一種數據結構。若是須要快速訪問數據,不多或不插入和刪除元素,就應該用數組;相反, 若是須要常常插入和刪除元素就須要用鏈表數據結構。
如何讓程序儘可能減小內存泄漏
Foundation
對象( OC
對象) : 只要方法中包含了 alloc\new\copy\mutableCopy\retain
等關鍵字,那麼這些方法產生的對象, 就必須在再也不使用的時候調用1次 release
或者1次 autorelease
。CoreFoundation
對象( C
對象) : 只要函數中包含了 create\new\copy\retain
等關鍵字, 那麼這些方法產生的對象, 就必須在再也不使用的時候調用1次 CFRelease
或者其餘 release
函數。CoreFoundation
對象( C
對象) : 只要函數中包含了 create\new\copy\retain
等關鍵字, 那麼這些方法產生的對象, 就必須在再也不使用的時候調用1次 CFRelease
或者其餘 release
函數。block的注意
// block的內存默認在棧裏面(系統自動管理)
void (^test)() = ^{
};
// 若是對block進行了Copy操做, block的內存會遷移到堆裏面(須要經過代碼管理內存)
Block_copy(test);
// 在不須要使用block的時候, 應該作1次release操做
Block_release(test);
[test release];
複製代碼
野指針舉例
建了個視圖控制器(ARC時)某個函數裏寫了以下代碼。當這個函數返回時由於沒有指針指向b因此b會被釋放、可是b.view不會被釋放。若是在b裏有須要操做b的地方(好比代理的方法),就會產生野指針(提早釋放)
B *b = [[B alloc]init];
[self.view addSubview:b.view];
複製代碼
set方法
@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;
// 上邊這句至關於下邊兩句
[_name release];
_name = nil;
}
複製代碼
引用計數的使用
int main(int argc, const char * argv[]) {
@autoreleasepool {
// 1
Person *p = [[Person alloc] init];
p.age = 20;
// 0 (p指向的內存已是壞內存, 稱person對象爲殭屍對象)
// p稱爲野指針, 野指針: 指向殭屍對象(壞內存)的指針
[p release];
// p稱爲空指針
p = nil;
p.age = 40;
// [0 setAge:40];
// message sent to deallocated instance 0x100201950
// 給空指針發消息不會報錯
[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對象(計數器==1) : 堆
Car *c = [[Car alloc] init];
}
// 當autoreleasepool執行完後後, 棧裏面的變量a\b\c都會被回收
// 可是堆裏面的Car對象還會留在內存中, 由於它是計數器依然是1
return 0;
}
複製代碼
看下面的程序,三次NSLog會輸出什麼?爲何?
結果:三、二、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:]後須要對這個數組作釋放操做嗎?
答: 不須要,這個對象被放到自動釋放池中
老版本的工程是能夠轉換成使用ARC的工程,轉換規則包括: