1.1 內存引用平衡原則
1) 如果使用alloc,new開頭,或者是copy(復制一個對象)來創建一個對象,意味著你擁有這個對象的所有權。這個對象的引用計數器初始值為1(也有可能>1)。
2) 如果你擁有這個對象的所有權,在不使用此對象時,就有責任向對象發送release消息。(誰創建了對象,誰就有責任release這個對象)
3) 如果並不擁有一個對象的所有權,而想要使用這個對象,為了防止你在使用此對象期間,對象被別人釋放掉,需要向對象發送retain消息,以保持對象。此時可以認為,你也擁有了這個對象所有權。
4) 當你使用完retain過的對象後,有責任release一下這個對象。
(誰retain了一個對象,誰就有責任release這個對象)
配對出現:(+1、-1 ==>平衡)
我們創建的對象不用了,就release;我們retain的對象不用了,就release。
內存管理的原則就是有加就有減。也就是說, 一次alloc(new)對應一次release, 一次retain對應一次release。
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:
ARC — 自動管理內存
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消息。
【iOS開發入門 ☞ OC語言·筆記五】的相關資料介紹到這裡,希望對您有所幫助! 提示:不會對讀者因本文所帶來的任何損失負責。如果您支持就請把本站添加至收藏夾哦!