這是個人第一篇文章,有什麼不對的地方還煩請你們指出啦,總以爲作開發頗有必要作筆記,方便記錄本身的所得也能與你們探討,可是以前一直比較懶因此無所做爲,那接下來的時間一塊兒好好努力吧各位加油加油。編程
瞭解內存管理的最普通的方式是考慮全部權。若是Manny對Jack曾說過alloc,retain或者copy,則表示Manny已經宣稱對Jack的全部權。多個對象能夠同時擁有Jack,但每一個對象只負責正確地管理本身對Jack的全部權,最終釋放Jack是每一個Jack全部者的責任,而Jack的非全部者永遠不須要釋放Jack。只要全部擁有Jack全部權的對象都以這種方式執行,Jack就不會泄漏也不會有任何指向Jack的指針留下來搖晃。安全
野指針:指針變量沒有進行初始化或指向的空間已經釋放。多線程
內存泄漏:框架
在ARC自動引用計數模式下,形成內存泄漏的狀況:函數
在MRC手動引用計數模式下,形成內存泄漏的狀況:優化
殭屍對象:堆中已經被釋放的對象(retainCount=0)。atom
空指針:指針賦值爲空(nil)。spa
一個變量的名稱,包括實例變量,只是一個指針。當你向該指針發送消息時,你實際上就是經過該指針將消息發送到它指向的對象。內存管理的規則是關於對象的原則,而不是關於名稱、引用或指針的規則。你不能遞增或遞減一個指針的保留計數,由於沒有這個東西。指針所佔用的內存是自動管理的(並且很小)。內存管理所關注的是指針所指向的對象。.net
Objective-C對象中保存着引用計數這一整數值。調用alloc或者retain方法後,引用計數+1。調用release後,引用計數-1。引用計數爲0時,調用dealloc方法廢棄對象。線程
對象操做 | Objective-C方法 | 引用計數 |
---|---|---|
生成並持有對象 | alloc/new/copy/mutablecopy | 1 |
持有對象 | retain | +1 |
釋放對象 | release | -1 |
廢棄對象 | dealloc | 0 |
本身生成的對象,本身持有
NSObject *obj = [[NSObject alloc] init];
[obj retain];
NSLog(@"obj - %lu",[obj retainCount]);
複製代碼
非本身生成的對象,本身也能持有
id obj = [NSMutableArray array];
[obj retain];
NSLog(@"obj - %lu",[obj retainCount]);
複製代碼
再也不須要本身持有的對象時釋放
NSObject *obj = [[NSObject alloc] init];//本身生成本身持有
[obj retain];
NSObject *obj2 = [obj retain];//非本身生成,本身持有
[obj release];
[obj2 release];
NSLog(@"obj - %lu",[obj retainCount]);
複製代碼
非本身持有的對象本身沒法釋放
id obj = [NSMutableArray array];
[obj release];
複製代碼
MRC下對象的引用計數能夠經過[object retainCount]方法得到。
autorelease故名思議就是自動釋放,看上去很像ARC,但其實更相似於C語言中的局部變量,也就是說,超出變量做用域的時候將自動被廢棄,但這裏與C語言不一樣的是,編程人員能夠手動設置其做用域。 autorelease的具體使用方法以下:
NSAutoreleasePool對象的生命週期至關於C語言變量的做用域,對於全部調用過autorelease方法的對象,在廢棄NSAutoreleasePool對象時,都將對對象統一調用release方法,代碼以下所示:
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc]init] ;
id obj = [ [NSObject alloc]init];
[obj autorelease] ;
[pool drain];
複製代碼
在Cocoa框架中,若是不是使用alloc/new/copy/mutablecopy這幾個方法返回的對象,其他方法返回的對象都將自動註冊到NSAutoreleasePool中,id array = [NSMutableArray arrayWithCapacity:10];其實也就等同於:id array = [[[NSMutableArray alloc] initWithCapacity:1] autorelease];
ARC是Objective-C編譯器的特性,而不是運行時特性或者垃圾回收機制,ARC所作的只不過是在代碼編譯時爲你自動在什麼時候的位置插入release或者autorelease,減小了開發的工做量。但咱們有時仍須要四種全部權修飾符來配合ARC來進行內存管理。
__strong:強引用,持有所指向對象的全部權,無修飾符狀況下的默認值。如需強制釋放,可置nil(這裏先建立一個指針,將新的值retain一次,將指針動態指向新的值,並將舊的值release一次)。
NSObject *obj = [[NSObject alloc]init];
//他們是等價的
NSObject __strong *obj = [[NSObject alloc]init];
複製代碼
__weak:弱引用,不持有所指向對象的全部權,引用指向的對象內存被回收以後,引用自己會置nil,避免野指針.避免循環引用,會將對象註冊到autoreleasepool(既不保留新值,也不釋放舊值,動態地將指針指向新的值,若是這個值剛被dealloc,就會將指針更新爲一個nil指針)。
unsafe_unretained:至關於assign。直接賦值。引用計數不變。他會發生野指針現象。因此不安全。不像weak,當指向的對象爲空的時候,將指針置爲nil。
_autoreleasing:將對象賦值給附有 _ autoreleasing 修飾符的變量等同於ARC 無效時調用對象的autorelease方法。
@autoreleasepool {
id __autoreleasing obj = [[NSObject alloc] init];
}
複製代碼
__strong底層實現
alloc/new/copy/mutablecopy狀況下
//alloc爲例的模擬底層代碼爲:
id __Strong obj = [[NSObject alloc] init];
id obj = objc_msgSend(NSobject, @selector(alloc));
objc_msgSend(obj, @selector(init));
objc_release(obj);
//編譯器自動幫咱們加入了release,來釋放對象
複製代碼
非alloc/new/copy/mutablecopy狀況下
//array爲例的模擬底層代碼爲:
id obj = objc_msgSend(NSMutableArray, @selector(array));
objc_retainAutoreleasedReturnValue(obj);
objc_release(obj);
//objc_retainAutoreleasedReturnValue(obj)是主要用於最優化程序運行
//array方法的底層模擬爲:
id obj = objc_msgSend(NSmutableArray,@selector(alloc));
objc_msgSend(obj,@selector(init));
return objc_autorealeaseReturnValue(obj);
複製代碼
objc_autorealeaseReturnValue(obj)與objc_retainAutoreleasedReturnValue(obj)是成對的,objc_autorealeaseReturnValue(obj)會把對象註冊到autorealeasepool中,可是若是它檢測到方法執行列表中出現objc_retainAutoreleasedReturnValue(obj)方法,那麼就不會將返回的對象註冊到autorealeasepool,而是直接傳遞到方法和函數的調用方。這樣直接傳遞能夠達到最優化。
__weak底層實現
__weak源碼
id __weak obj1 = obj;
//模擬代碼
id obj1;
obj1 = 0;
objc_storeWeak(&obj1,obj);
objc_destoryWeak(&obj1); 等同於objc_storeWeak(&obj1,0);
複製代碼
objc_storeWeak(&obj1,obj)函數將第二個參數的賦值對象的地址做爲鍵值,將第一個參數的附有__weak修飾符的變量的地址註冊到weak表中,若是第二個參數爲0,則把變量的地址從weak中刪除。一個鍵值能夠註冊多個變量的地址因而可知,若是大量的weak變量,則會消耗CPU資源,因此weak 只用來避免循環引用。
__weak與@autoreleasepool
id __weak obj1 = obj;
NSLog(@"%@",obj1);
//模擬代碼
id obj1;
objc_initWeak(&obj1,obj);
id temp = objc_loadWeakRetained(&obj1);
objc_autorelease(temp);
NSLog(@"%@",temp);
objc_destoryWeak(&obj1);
複製代碼
因此在@autorealeasepool塊結束前能夠放心使用weak修飾變量
__autoreleasing底層實現
將對象賦值給附有__autoreleasing修飾符的變量等同於ARC無效時,調用對象的autorelease方法。
使用alloc/new/copy/mutableCopy時
@autoreleasepool {
id __autoreleasing obj = [[NSObject alloc] init];
}
//模擬代碼
id pool = objc_autoreleasePoolPush();
id obj = objc_msgSend(NSObject, @selector(alloc));
objc_msgSend(obj, @selector(init));
objc_autorelease(obj);
objc_autoreleasPoolPop(pool);
複製代碼
使用alloc/new/copy/mutableCopy之外的方法時
@autoreleasepool {
id __autoreleasing obj = [NSMutableArray array];
}
// 模擬代碼
id pool = objc_autoreleasePoolPush();
id obj = objc_msgSend(NSMutableArray,@selector(array));
objc_retainAutoreleasedReturnValue(obj);
objc_autorelease(obj);
objc_autoreleasPoolPop(pool);
// 雖然 obj 持有對象的方法變爲 objc_retainAutoreleasedReturnValue, 可是將 obj 所引用的對象註冊到 autoreleasepool 中的方法並無改變
複製代碼
關於@autoreleasepool,在ARC下應該使用@autoreleasepool而不是NSAutoreleasePool,@autoreleasepool的具體實現將在以後的文章中繼續探討。
ObjC2.0引入了@property,提供成員變量訪問方法、權限、環境、內存管理類型的聲明,下面主要說明ARC中屬性的內存管理。屬性的參數分爲三類,基本數據類型默認爲(atomic,readwrite,assign),對象類型默認爲(atomic,readwrite,strong),其中第三個參數就是該屬性的內存管理方式修飾,修飾詞能夠是如下之一:
assign
直接賦值,通常用來修飾基本數據類型。固然也能夠修飾ObjC對象,可是不推薦,由於被assign修飾的對象釋放後,指針仍是指向釋放前的內存,在後續操做中可能會致使內存問題引起崩潰。
@property (nonatomic, assign) NSInteger count;
複製代碼
retain
retain和strong同樣,都用來修飾ObjC對象,使用set方法賦值時,實質上是會先保留新值,再釋放舊值,再設置新值,避免新舊值同樣時致使對象被釋放的的問題。
//MRC寫法以下
- (void)setCount:(NSObject *)count {
[count retain];
[_count release];
_count = count;
}
//ARC對應寫法
- (void)setCount:(NSObject *)count {
_count = count;
}
複製代碼
copy
通常用來修飾String、Dict、Array等須要保護其封裝性的對象,尤爲是在其內容可變的狀況下,所以會拷貝(深拷貝)一分內容給屬性使用,避免可能形成的對源內容進行改動。使用set方法賦值時,實質上是會先拷貝新值,再釋放舊值,再設置新值。實際上,遵照NSCopying的對象均可以使用copy,固然,若是你肯定是要共用同一份可變內容,你也可使用strong或retain。
weak
ARC新引入修飾詞,可代替assign,比assign多增長一個特性(置nil)。weak和strong同樣用來修飾ObjC對象。使用set方法賦值時,實質上不保留新值,也不釋放舊值,只設置新值。
@property (weak) id<MyDelegate> delegate;
複製代碼
strong
ARC新引入修飾詞,可代替retain,ARC通常都寫strong。
Person *per = [[Person alloc] init];
self.person = per;
若是是strong,對象的retainCount爲2,若是爲weak,對象的retainCount爲1。
unsafe_unretained
等價於assign,能夠用來修飾數據類型和OC對象,可是不會使計數器加1,且對象銷燬時也不會將對象指向nil,容易形成野指針錯誤。
OC中使用block必須本身管理內存,錯誤的內存管理將致使循環引用等內存泄漏問題,這裏主要說明在ARC下block聲明和使用的時候須要注意的兩點:
若是你使用@property去聲明一個block的時候,通常使用copy來進行修飾(固然也能夠不寫,編譯器自動進行copy操做),儘可能不要使用retain。
@property (nonatomic, copy) void(^block)(NSData * data);
複製代碼
block會對內部使用的對象進行強引用,所以在使用的時候應該肯定不會引發循環引用,固然保險的作法就是添加弱引用標記。
__weak typeof(self) weakSelf = self;
複製代碼