1.1 內存引用平衡原則程序員
1) 若是使用alloc,new開頭,或者是copy(複製一個對象)來建立一個對象,意味着你擁有這個對象的全部權。這個對象的引用計數器初始值爲1(也有可能>1)。objective-c
2) 若是你擁有這個對象的全部權,在不使用此對象時,就有責任向對象發送release消息。(誰建立了對象,誰就有責任release這個對象) 安全
3) 若是並不擁有一個對象的全部權,而想要使用這個對象,爲了防止你在使用此對象期間,對象被別人釋放掉,須要向對象發送retain消息,以保持對象。此時能夠認爲,你也擁有了這個對象全部權。函數
4) 當你使用完retain過的對象後,有責任release一下這個對象。性能
(誰retain了一個對象,誰就有責任release這個對象)優化
配對出現:(+一、-1 ==>平衡) atom
咱們建立的對象不用了,就release;咱們retain的對象不用了,就release。spa
內存管理的原則就是有加就有減。也就是說, 一次alloc(new)對應一次release, 一次retain對應一次release。3d
1.2 自動釋放池(autoreleasepool)指針
經過自動釋放池來管理對象,只須要一個自動釋放池,能夠管理不少對象,當自動釋放池結束的時候,會自動向池中的每一個對象都發送release消息。
1) 若是一個對象建立後,不能立刻釋放它,但又不得不盡到釋放對象的責任,此時能夠將對象放入自動釋放池,延遲對象的釋放時機。好比絕大部分工廠方法都是如此。工廠方法中的對象是方法中建立的,按理來講應該由工廠方法內部釋放,但工廠方法的功能決定了這個對象不能立刻釋放,此時應該將對象放入自動釋放池。
2) 當自動釋放池結束時,會向池中的全部對象發送release消息,若是此時,池中的對象的引用計數器是1,那麼,對象會被釋放掉。
3) 如何開始和結束一個自動釋放池呢?
//自動釋放池,用於回收對象的存儲空間。 @autoreleasepool{ //開始(建立一個自動釋放池) …… …… } //結束(自動釋放池銷燬了, 給自動釋放池中全部的對象發送一條release消息) |
還有一種性能低下,被淘汰的自動釋放池建立方式(瞭解)
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc]init];//開始一個自動釋放池 …… …… [pool drain];//結束一個自動釋放池 |
4) 不管一個對象是否在自動釋放池,只要這個對象是由別人建立的,你要使用此對象,就得retain,用完此對象,就得release,即便對象在自動釋放池,也依然如此。
5) 實際開發中,合理使用自動釋放池來避免內存使用出現峯值。
若是App出現的內存使用的峯值,此時才考慮是不是因爲大量使用工廠方法形成的,是否須要使用自動釋放池解決問題。要合理使用自動釋放池,大量使用會消耗系統資源。
6) autorelease方法:在自動釋放池中使用,目的是將對象添加到自動釋放池中。自動釋放池銷燬時會對池中對象做release操做。(延遲release)
@autoreleasepool { Person *p = [[[Person alloc] init] autorelease];
} |
// 銷燬自動釋放池的時候 要對person再執行release操做的話 會報野指針錯誤 @autoreleasepool { Person *person = [[[Person alloc] init] autorelease]; [person release]; } |
// 對象執行兩次autorelease意味着自動釋放池銷燬的時候 對象會執行兩次release操做 會報野指針錯誤 @autoreleasepool { Person *person = [[[[Person alloc] init] autorelease] autorelease]; } |
1.3 setter方法的內存管理(應用場景:兩個類是聚合關係)
當一個方法傳入一個對象後,若是須要將這個對象用實例變量等手段保存起來持續使用時,須要作如下事:
1) 先將此對象的引用計數器加1(retain)
2) 再將原來實例變量指向的對象的引用計數器減1(release)
3) 最後將傳入的對象地址保存到實例變量中
4) 被retain的對象一般須要在dealloc方法中release.
只要一個對象想使用房間,就須要對這個房間的引用計數器+1
只要一個對象不想使用房間,就須要對這個房間的引用技術器-1
常常會被問到的問題:
1) 前面3個順序能否顛倒?
能不能先release原來的對象,再賦值,最後retain新對象? 通常能夠,但不建議
2) dealloc方法中,可不可使用self.屬性 = nil;的方式釋放屬性所指向的對象? ok的
能夠的,self.屬性 至關於調用上面的setter方法(該方法中有向對象發release)
Demo:setter方法的內存管理
1.4 property修飾符
1) nonatomic 非原子性操做 (安全性低,但效率高。iOS實際開發中99%用這個,以確保性能)
atomic 原子性操做 (安全性高,但很耗費資源)
注意,默認是原子性的(atomic),因此在定義屬性時必定要寫上nonatomic。
2) assign, retain, copy
assign 是默認值,僅作賦值。不會解決內存問題(即不會retain,也不會release)。在MRC中可用於對象類型和非對象類型。
retain 只能用於對象類型的屬性,會解決內存問題(生成的setter方法會自動加入retain,release等內存操做代碼)
copy 一些特殊對象類型,若是不但願和別人共享一個對象,用copy會自動建立一個新的對象。
有些屬性在傳入後,須要拷貝一份,以防止傳入的對象內容被修改後,影響到個人對象。
(並非全部的對象都能拷貝,只有遵照<NSCopying>協議,實現了協議中CopyWithZone方法的這種對象才擁有拷貝的功能)
3) readonly/readwrite
readwrite 是默認的,編譯器生成getter和setter方法
readonly 只讀,編譯器只生成getter方法
屬性訪問器的類型:
注:聲明屬性默認狀況下,並無解決內存問題
當使用 @property(retain)引用數據類型…,幫咱們解決了setter使用中的內存問題,但dealloc中的release操做,咱們本身來作。
1.5 MRC中的循環引用(兩個對象互相引用(即互相包含)時,要一強一弱!)
若是A對象要擁有B對象, 而B對象又要擁有A對象, 此時會造成循環retain
如何解決這個問題:讓其中一方不要作retain操做便可!
Demo:
1.1 概念
Automatic Reference Counting 自動引用計數
基於MRC, 在MRC下,對象的引用計數是由程序員負責的。在ARC下,對象的引用計數工做由編譯器來完成。
1.2 ARC的工做原理
在ARC下,堆內存空間的管理依然使用引用計數器的方式。只是ARC下的引用計數器的加減工做再也不由程序員來作,而是由編譯器來作。
編譯器在編譯程序期間,會在程序恰當的位置自動加入對對象的retain,release和autorelease的操做。
注意:ARC是 編譯期語法或編譯器特性(便可以理解爲是Xcode的一個功能),而不是運行時特性。
1.3 怎麼用
程序員不要對對象的引用計數器進行操做,編譯器會幫咱們作:
1) 在ARC下,不要在程序中調用retain, release, autorelease方法。
2) 在ARC下,不要重寫retain, release, autorelease方法。
3) 在ARC下,不要在dealloc方法中調用父類的dealloc方法。實際上,ARC下,dealloc方法基本沒有用了。
總之,一切與內存操做相關的東西都由ARC自動完成。
1.4 ARC的判斷原則:(即系統怎麼判斷對象是否要釋放)
TRPerson *p1 = [[TRPerson alloc]init]; __strong TRPerson *p2 = [[TRPerson alloc]init]; |
__weak TRPerson *p = [[TRPerson alloc]init]; |
//ARC的判斷準則:只要沒有強指針指向對象,對象就會釋放 { TRPerson *p1 = [[TRPerson alloc]init]; __strong TRPerson *p2 = [[TRPerson alloc]init]; __weak TRPerson *p3 = p2; p2 = nil;//p2改變了指向,此時就沒有強指針指向對象,對象就會釋放 }//出了大括號,局部變量p1就釋放,此時就沒有強指針指向對象,對象就會釋放 |
單個對象的內存管理:若是一個對象再也不使用了,就把指向對象的強指針置爲nil,對象就會釋放。
{ //p是弱指針,對象會被當即釋放 __weak TRPerson *p = [[TRPerson alloc]init];//剛建立就被釋放! } |
1.5 ARC中多個對象的內存管理:
@class TRDog; @interface TRPerson : NSObject
//MRC下寫法 @property (nonatomic, retain) TRDog *dog; //ARC下寫法 @property (nonatomic, strong) TRDog *dog;
@end |
MRC
A對象想使用B對象, 須要對B對象進行一次retain
A對象不用B對象了, 須要對B對象進行一次release
即,property的時候進行retain, dealloc的時候進行release
ARC
A對象想使用B對象, 那麼就須要用一個強指針指向B對象(即用strong,表示強引用)
// 在ARC中保存一個對象用strong, 至關於MRC中的retain
@property (nonatomic, strong) Dog *dog;
A對象不用B對象了, 什麼都不須要作, 編譯器會自動幫咱們作
1.6 ARC下循環引用問題
循環引用:指兩個對象相互強引用了對方,即retain了對方,從而致使誰也釋放不了誰的內存泄露問題。
@class TRDog; @interface TRPerson : NSObject //MRC寫法 //@property (nonatomic, retain) TRDog *dog; //ARC寫法 @property (nonatomic, strong) TRDog *dog; @end
@interface TRDog : NSObject //錯誤寫法,循環引用會致使內存泄露 //@property(nonatomic, strong)TRPerson *owner;
//正確寫法,當若是保存的是對象類型建議使用weak //@property(nonatomic, assign)TRPerson *owner; @property (nonatomic, weak) TRPerson *owner; @end |
1.7 若是在ARC下定義屬性的內存特質(attribute)
(1)strong 強引用
相似於retain,引用時候會引用計數+1。
strong 案例: |
@property (nonatomic, strong) NSString *str1; @property (nonatomic, strong) NSString *str2; |
self.str1 = @"Hello World"; self.str2 = self.str1; self.str1 = nil; NSLog(@"str2 = %@", self.str2); |
結果是:str2 = Hello World |
(2)weak 弱引用
weak 案例: |
@property (nonatomic, strong) NSString *str1; @property (nonatomic, weak) NSString *str2; |
self.str1 = @"Hello World"; self.str2 = self.str1; self.str1 = nil; NSLog(@"str2 = %@", self.str2); |
結果是:str2 = null |
(3)unsafe_unretained (從名字上來看:不安全,不retain)
unsafe_unretained 案例: |
@property (nonatomic, strong) NSString *str1; @property (nonatomic, unsafe_unretained) NSString *str2; |
self.str1 = @"Hello World"; self.str2 = self.str1; self.str1 = nil; NSLog(@"str2 = %@", self.str2); |
沒有輸出結果!程序崩掉,會報野指針錯誤(EXC_BAD_ACCESS 壞訪問) |
(4)copy 拷貝 (和之前的copy同樣)
使用copy: 對NSString
效果其實和retain沒什麼兩樣,惟一的區別就是copy只用於NSString而不能用於NSMutableString, 若是當一個類繼承NSObject,那麼這個類裏面的屬性須要使用copy。
retain是指針拷貝,copy是內容拷貝。
若是想聲明臨時變量就得用__strong, __weak, __unsafe_unretained, __autoreleasing,其用法與上面介紹的相似。
案例: |
__strong NSString *str1 = @"Hello World"; __weak NSString *str2 = str1; __unsafe_unretained NSString *str3 = str2; str1 = nil; //如今str1與str2的指針都爲nil,而str3不爲nil,可是是野指針。 |
提示:沒有兩個下劃線的放屬性裏,有兩個下劃線的放變量前。
autoreleasing的應用:在函數內部申請的空間,在函數外部也可使用
//MRC下 -(NSString *)stringTest { NSString *retStr = [NSString stringWithString:@"test"]; return [[retStr retain] autorelease]; } |
//ARC下 -(NSString *)stringTest { __autoreleasing NSString *retStr = [NSString alloc] initWithString:@"test"]; return retStr; } |
ARC下@property參數:
strong:用於OC對象,至關於MRC中的retain (強引用)
weak:用於OC對象,至關於MRC中的assign (弱引用)
assign:用於基本數據類型,跟MRC中的assign同樣。
1.8 ARC下的自動釋放池
在ARC下,對象的工廠方法依然會將對象放入自動釋放池。當池結束時,向池中的對象發送release消息。
池中的對象何時銷燬,沒法肯定,由於編譯器會作不少優化。
在iOS開發中,一個事件循環結束,自動釋放池會釋放一次,池中的對象會收到release消息。