------- <ahref="http://www.itheima.com" target="_blank" rel="external nofollow" target="_blank" rel="external nofollow" target="blank">android教育訓練</a>、<ahref="http://www.itheima.com" target="_blank" rel="external nofollow" target="_blank" rel="external nofollow" target="blank">java教育訓練</a>、期待與您交流! ----------
1. 什麼是記憶體管理?
由于我們程式運作的任何裝置的記憶體都是有限的,是以合理的安排記憶體,處理好記憶體的問題,是一個非常重要的技術。當一個對象已經不再使用,例如植物大戰僵屍中的子彈,當它飛出螢幕時,我們就要及時的讓它釋放占用的記憶體。這樣就為我們提高了記憶體的利用效率。如果一個APP不能及時的釋放廢棄的空間時,當它占用較大的記憶體是,系統就會發出警告,這是就要及時的處理掉“垃圾”。
資料或者對象一般會存放到堆或者棧中。棧中存放内容一般都會自動釋放,而棧中的内容就需要我們去手動釋放。
在學習這部分的内容時,程式設計中要關閉Xcode中的ARC機制。在Xcode6.2版本中,建立項目是并不會讓你去選擇是否帶ARC,而要在建立好項目之後進行取消。具體方法是:首先建立好項目,然後點項目名,在Building Setting中可以找到Apple LLVM 6.0 Language-Object c,在此架構下你可以看到Object c Automatic Reference Counting,選擇NO 就可以了。
2. 引用計數器
每個oc對象都有一個引用計數器。引用計數器用來判斷此對象在什麼時候釋放記憶體。當某個oc對象的引用計數器為0時,那麼oc對象占用的記憶體将會被釋放。每個引用計數器都占用4個位元組的存儲空間。
3. 記憶體管理常用的關鍵字
(1)alloc:用來建立一個對象空間。一般它都會配合init的使用,比如:[[NSObject alloc] init],建立一個類型為NSObject的對象,并對其進行初始化。
(2)release:給對象發送一個release消息,對象的引用計數器減1。一般配合retain的使用,一個retain就有一個release。
(3)retain:給對象發送一個retain消息,對象的引用計數器加1。
(4)retainCount:擷取對象目前的引用計數器數值,傳回值類型為長整型。
(5)autorelease:會将調用的對象放到一個自動釋放池中。當釋放池結束時,對象會自動調用一次release,引用計數器減1。在調用此指令是,對象引用計數器并不會立即減1。有傳回值,傳回值類型為對象本身。而且autorelease必須放到自動釋放池(釋放池是以棧的形式存放,先進後出)中使用。例如:
//自動釋放池
@autoreleasepool {
User *suer =[[[User alloc] init] autorelease];
} //自動釋放池結束。
它的缺點是不能精确的釋放,是以使用時占用記憶體較大的對象不要使用autorelease。
(6)dealloc:相當于對象的“遺言”。在對象被銷毀時,對象會自動調用dealloc。而且在調用dealloc時,一定會有調用[super dealloc],而且一定要放到最後調用。
4. 幾個要注意的概念
(1)空指針:沒有指向任何對象的指針,也就是說指針指向的内容為nil、null或者0。
(2)僵屍對象:所占用記憶體已經被回收的對象,而且僵屍對象不能再使用。
(3)野指針:指向僵屍對象的指針。
利用下邊的程式來看一下這些概念:
#import <Foundation/Foundation.h>
@interface Person : NSObject
@end
@implementation Person
- (void)dealloc
{
NSLog(@"Person對象銷毀");
[super dealloc];
}
@end
int main()
{
//定義了一個空指針。
int *p = nil;
//定義一個Person對象,引用計數器為1。
Person *p1 = [[Person alloc] init];
//進行一次release,對象引用計數器變為0。此時,指針p1已經是野指針,因為對象已經銷毀。
[p1 release];
//将野指針指向空,變成空指針。
p1 = nil;
//對空指針進行release是不會報錯的。
[p1 release];
[p1 release];
return 0;
}
在出現野指針錯誤時,一般會報錯為:EXC-BAD-ACCESS。是以,需要牢記,下次遇見這樣的報錯就知道什麼原因了。
5. 記憶體管理的一些總結
(1)想要使用一個對象,就必須讓對象的引用計數器加1(也就是引用retain操作)。
(2)有加就有減。如果用alloc、new或者copy建立一個對象,必須調用release或者autorelease對對象進行一次引用計數器減1。
(3)不想使用對象,就應該讓對象的計數器-1,也就是讓對象進行一次release。
(4)誰retain,誰release;誰alloc,誰release。
6. set方法記憶體管理
其實,在調用set方法時,如果有傳遞參數,而且傳遞參數為對象,就不得不考慮對象的引用計數器的加減。對于,基本資料類型參數,set設定方法為:
//基本資料類型,set方法直接指派
- (void)setAge
{
_age = age;
}
但是對于對象,就要進行進一步的考慮:
//傳遞的參數為對象類型
- (void)setDog:(Dog *)dog
{
//判斷時候為已經擁有的對象。防止出現重複指向時,使對象變為僵屍對象。
if (self.dog != dog)
{
//要放棄的對象進行-1
[_dog release];
//新對象+1,然後指派給成員變量
_dog = [dog retain];
}
}
7. @property參數寫法
@property參數的類型主要分為4類,在進行@property參數書寫時,不同類型的參數可以同時存在,但相同類型的參數隻能存在一個。
(1)記憶體管理相關參數:
retain:表示進行對象的set方法,即set方法中的第二中方法;
assign:一般對于基本資料類型,進行直接指派操作。當然,在類的互相引用時,對已對象類型的參數,也要用assign。預設情況為此類型。
copy:對舊對象進行一次release,對新對象進行copy。
(2)是否生成set方法:
readonly:隻生成set方法,不生成聲明;
readwrite:可讀可寫,預設情況下為此方法。
(3)多線程管理
nonatomic:可以提高性能,以後寫程式時,必須加上這個。否則就是atomic。
atomic:性能低,而且預設情況下為此形式。
(4)set方法和get方法的名稱
getter = 方法名, setter = **:,不可漏了”:“。
如下是一個簡單的例子:
@property (nonatomic, retain) NSString *text;
@property (nonatomic, retain) NSString *peitu;
@property (nonatomic, assign) time_t time;
@property (nonatomic, retain) User *user;
8. 循環引用
在循環引用時,不可能利用#import進行頭檔案的複制。是以,為了解決此問題,引入了@class。
@class ***;在.h檔案中僅僅是告訴編譯器,***是一個類。并沒有對其進行複制,是以在.m檔案中還要對其進行#import操作。利用@class可以提高程式性能,同時解決了循環引用的問題。
另外就是循環return的問題,在類與類之間的循環return會使對象不能夠釋放。解決方法是一端用retain,而另一端用assign。
9.ARC機制
ARC是編譯器特性,不是垃圾回收。在編譯時,自動檢測哪裡需要插入release。
ARC判斷标準:隻要沒有強指針指向對象,那麼對象就會釋放。
指針分類:
(1)強指針:預設情況下所有的指針都是強指針,聲明是利用__strong來聲明。
(2)弱指針:不能決定對象是否釋放。定義時用__weak聲明。