黑馬程式設計師_OC之記憶體管理
阿新 • • 發佈:2019-02-15
記憶體管理
------ IOS培訓、android培訓、期待與您交流!-------
1、記憶體管理的定義
1>、移動移動裝置的記憶體極其有限,每個app所能佔用的記憶體是有限制的
2>、當app所佔用的記憶體較多時,系統會發出記憶體警告,這時得回收一些不需要再使用的記憶體空間。比如回收一些不需要使用的物件、變數等
3>、管理範圍:任何繼承了NSObject的物件,對其他基本資料型別(int、char、float、double、struct、enum等)無效
2、計數器的作用
1>、每個OC物件都有自己的引用計數器,表示“物件被引用的次數”,即有多少人正在使用這個OC物件
2>、當一個物件的引用計數器值為0時,物件佔用的記憶體就會被系統回收。換句話說,如果物件的計數器不為0,那麼在整個程式執行過程,它佔用的記憶體就不可能被回收,除非整個程式已經退出
3>、當使用alloc、new或者copy建立一個新物件時,新物件的引用計數器預設就是1
3、計數器的操作
1>、給物件傳送一條retain訊息,可以使引用計數器值+1(retain方法返回物件本身)
2>、給物件傳送一條release訊息,可以使引用計數器值-1
3>、可以給物件傳送retainCount訊息獲得當前的引用計數器值
4、物件的銷燬
1>、當一個物件的引用計數器值為0時,那麼它將被銷燬,其佔用的記憶體被系統回收
2>、當一個物件被銷燬時,系統會自動向物件傳送一條dealloc訊息
一般會重寫dealloc方法,在這裡釋放相關資源,dealloc就像物件的遺言
一旦重寫了dealloc方法,就必須呼叫[super dealloc],並且放在最後面呼叫
不要直接呼叫dealloc方法
3>、一旦物件被回收了,它佔用的記憶體就不再可用,堅持使用會導致程式崩潰(野指標錯誤)
二、引用計數器的基本操作:
在main.h檔案中
#import <Foundation/Foundation.h>
#import "Person.h"
int main()
{
Person *p= [[Person alloc] init]; //計數器為1
NSUInteger c = [p retainCount];
NSlog(@"計數器:%ld",c);
[p retain];//計數器為2
[p release]; //計數器為1
return 0;
}
驗證物件是否被回收,在Person.m檔案中重寫dealloc方法,在dealloc中列印,dealloc方法一定要呼叫super,否則會發出警告;
#import "Person.h"
@implementation Person
//當一個Person物件被回收的時候,就會自動呼叫這個方法
- (void)dealloc
{ //super的dealloc一定要呼叫,而且放在後面
NSLog(@"Person物件被回收");
[super dealloc];
}
@end
1、小結:1> retain :計數器+1,會返回物件本身
2> release :計數器-1,沒有返回值
3> retainCount :獲取當前的計數器
4> dealloc
* 當一個物件要被回收的時候,就會呼叫
* 一定要呼叫[super dealloc],這句呼叫要放在最後面
- (void)dealloc
{
NSLog(@"物件被回收");
// super的dealloc一定要呼叫,而且放在最後面
[super dealloc];
}
2、概念:
1>、殭屍物件 :所佔用記憶體已經被回收的物件,殭屍物件不能再使用
2>、 野指標 :指向殭屍物件(不可用記憶體)的指標,給野指標傳送訊息會報錯(EXC_BAD_ACCESS)
3> 、空指標 :沒有指向任何東西的指標(儲存的東西是nil、NULL、0),給空指標傳送訊息不會報錯;
三、多物件記憶體管理
1.你想使用(佔用)某個物件,就應該讓物件的計數器+1(讓物件做一次retain操作)
2.你不想再使用(佔用)某個物件,就應該讓物件的計數器-1(讓物件做一次release)
3.誰retain,誰release
4.誰alloc,誰release
@property會遮蔽一些記憶體管理細節
5.程式碼例項
在Book.h檔案中
#import<Foundation/Foundation.h>
@interface Book : NSObject
{
int _price;
}
- (void)setPrice : (int)price;
- (int)price;
@end
在Book.m檔案中
#import "Book.h"
@implementation Book
- (void)setPrice : (int)price
{
_price = price;
}
- (int)price
{
return _price;
}
- (void)dealloc
{
NSLog(@"Book物件被回收");
[super dealloc];
}
@end
在Person.h檔案中
#import<Foundation/Foundation.h>
#import "Book.h"
@interface Person : NSObject
{
Book *_book;
}
- (void)setBook : (Book *)book;
- (Book *)book;
@end
在Person.m檔案中
#import"Person.h"
@implementation Person
- (void)setBook : (Book *)book
{
_book = [book retain];//物件計數器加1
}
- (Book *)book
{
}
- (void)dealloc
{
[_book release];
NSLog(@"Person物件被回收");
[super dealloc];
}
@end
在main.m檔案中
#import<Foundation/Foundation.h>
#import"Person.h"
#import "Book.h"
int main()
{
Book *b = [[Book alloc] init];
Person *p1 = [[Person alloc] init];
[p1 setBook:b];//p1想佔用b這本書
[p1 release];//release只是計數器見,並不是釋放物件
[b release]; //有alloc必須有release
return 0;
}
四、set方法中記憶體管理
1.set方法的程式碼規範
1> 基本資料型別:不需要管理記憶體,直接複製
- (void)setAge:(int)age
{
_age = age;
}
2> OC物件型別
- (void)setCar:(Car *)car
{
// 1.先判斷是不是新傳進來物件
if ( car != _car )
{
// 2.對舊物件做一次release
[_car release];
// 3.對新物件做一次retain
_car = [car retain];
}
}
注:1>如果新物件和舊物件一樣,沒用
2>、如果物件不是由alloc產生的,就不需要release
2.dealloc方法的程式碼規範
1> 一定要[super dealloc],而且放到最後面
2> 對self(當前)所擁有的其他物件做一次release
- (void)dealloc
{
[_car release];
[super dealloc];
}
3.property引數:property生成的set和get方法是最簡單的方法,是直接賦值,並不管記憶體。
1>、使用@property時,@property (retain) Book *book;
retain: 生成的set方法中,release舊值,retain新值(用於OC物件)
相當於下列的程式碼: [_book release];
_book = [book retain];
//釋放到以前的名稱,在重新賦值
//property中生成retain,dealloc中實現release,故還需要寫dealloc方法,set方法可以用@property實現
//需要在實現中重寫dealloc
- (void)dealloc
{
[_namerelease];
[superdealloc]; //注意一定要放在最後
}
4、記憶體管理相關的引數
1>、retain: release舊值,retain新值(用於OC物件)
2>、assign:直接賦值,不做任何記憶體管理(預設,用於非OC物件型別)
3>、copy :release舊值,copy新值(一般用於NSString *)
5、是否要生成set方法
readwrite:同時生成set方法和get方法的宣告和實現(預設)
readonly :只會生成get方法的宣告和實現(只讀)
注:上述的關鍵字可以寫在一起:@property (readwrite assign) int height;
6、多執行緒管理
atomic :效能低(預設)
nonatomic:效能高(一般用這個)
7、控制set方法和get方法的名稱
setter:設定set方法的名稱,一定有個冒號:
getter:設定get方法的名稱(一般用在bool型別)
@property (getter = abc,set = setAbc:) int weight;
@property (getter = isRich )BOOL rich;
//返回bool型別的方法名一般以is開頭
8、模型設計
在User.h檔案中
/* 作者:MJ
描述:微博使用者
時間:
檔名:User.h */
#inport<Foundation/Foundation.h>
typedef enum{
SexMan,
SexWoman
}Sex;
typedef struct{
int year;
int month;
int day;
}Date;//結構體並不是物件
@interface User : NSObject
@property (nonatomic, retain) NSString *name;
@property (nonatomic, retain) NSString *account;
@property (nonatomic, retain) NSString *password;
//頭像,存路徑 http://weibo.com/a.png URL
@property (nonatomic, retain) NSString *icon; //頭像
@property (nonatomic,assign ) Sex sex; //性別
@property (nonatomic, retain) NSString *phone; //電話
@property (nonatomic, assign) Date birthday;
@end
在User.m檔案中
@import "User.h"
@implementation User
- (void)dealloc
{
[_name release];
[_account release];
[_icon release];
[_password release];
[super dealloc];
}
@end
//如果在User.h檔案中有很多@property,在這裡需要就需要寫很多release,太麻煩了,所以設計一個類Status表示微博
在Status.h檔案中
#import<Foundation/Foundation.h>
//微博內容、微博配圖、傳送時間、轉發的微博、被評論數、被轉發數
@interface Status : NSObject
@property (nonatomic, retain) NSString *text;
@property (nonatomic, retain) NSString *icon; //配圖
@property (nonatomic, assign) time_t time;
//time_t是C語言表示時間的資料型別,其實就是long
@property (nonatomic, retain) User *user;
@property (nonatomic, retain) Status *retweetStatus;
//轉發的微博
@property (nonatomic, assign)int commentCount;//評論數
@property (nonatomic, retain) Status *retweetCount;
//轉發微博數
@end
在Status.m檔案中
#import "Status.h"
@implementation Status
- (void)dealloc
{
[_text release];
[_user release];
[_retweetStatus release];
[super dealloc];
}
@end
在main.m檔案中
#import<Foundation/Foundation.h>
@import "User.h"
#import "Status.h"
int main()
{
//新建兩條微博
Status *s = [[Status alloc] init];
s.text = @"今天天氣真好!!!";
Status *s2 = [[Status alloc] init];
s2.text = @"今天天氣真的很好!";
s2.retweetStatus = s;
//新建兩個使用者
User *u = [[User alloc] init ];
u.name = @"AAA";
s.user = u;
User *u2 = [[User alloc] init ];
u2.name = @"BBB";
s2.user = u2;
[s2 release];
[s release];
return 0;
}
五、迴圈引用:類與類之間的迴圈引用
1. @class使用場景:對於迴圈依賴關係來說,比方A類引用B類,同時B類也引用A類
這種程式碼編譯會報錯。但是當使用@class在兩個類相互宣告,就不會出現編譯報錯
2. 用法概括:
1>、使用 @class 類名; 就可以引用一個類,說明一下它是一個類
2>、開發中引用一個類的規範
在.h檔案中用@class來宣告類
在.m檔案中用#import來包含類的所有東西
3>、和#import的區別
#import方式會包含被引用類的所有資訊,包括被引用類的變數和方法;
@class方式只是告訴編譯器在A.h檔案中 B *b 只是類的宣告,具體這個類裡有什麼資訊,這裡不需要知道,等實現檔案中真正要用到時,才會真正去檢視B類中資訊
4>、如果有上百個標頭檔案都#import了同一個檔案,或者這些檔案依次被#improt,那麼一旦最開始的標頭檔案稍有改動,後面引用到這個檔案的所有類都需要重新編譯一遍,這樣的效率也是可想而知的,而相對來講,使用@class方式就不會出現這種問題了
5>、在.m實現檔案中,如果需要引用到被引用類的實體變數或者方法時,還需要使用#import方式引入被引用類
3.迴圈retain
比如A物件retain了B物件,B物件retain了A物件
這樣會導致A物件和B物件永遠無法釋放
解決方案當兩端互相引用時,應該一端用retain、一端用assign
六、 autorelease自動釋放池
1、autorelease的基本用法
1> 會返回物件本身
Person *p = [[[Person alloc] init] autolease]
2> 會將物件放到一個自動釋放池中
3> 當自動釋放池被銷燬時,會對池子裡面的所有物件做一次release操作,不用再每次都對物件release
4>、@autoreleasepool
{ //開始代表建立了一個釋放池
Person *p = [[[Person alloc] init] autolease]
}//結束代表銷燬
5> 呼叫完autorelease方法後,物件的計數器不變
2、autorelease的好處
1> 不用再關心物件釋放的時間
2> 不用再關心什麼時候呼叫release
3、autorelease的使用注意
1> 佔用記憶體較大的物件不要隨便使用autorelease
2> 佔用記憶體較小的物件使用autorelease,沒有太大影響
4、常見的錯誤
1> alloc之後呼叫了autorelease,又呼叫release
@autoreleasepool
{
// 1
Person *p = [[[Person alloc] init] autorelease];
// 0
[p release];
}//-1
這樣會發生野指標錯誤
2> 連續呼叫多次autorelease
@autoreleasepool
{
Person *p = [[[[Person alloc] init] autorelease] autorelease];
}
5、自動釋放池
1> 在iOS程式執行過程中,會建立無數個池子。這些池子都是以棧結構存在(先進後出)
2> 當一個物件呼叫autorelease方法時,會將這個物件放到棧頂的釋放池
6、程式碼案例:
int main()
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
Person *pp = [[[Person alloc] init] autorelease];
[pool release]; // [pool drain];
@autoreleasepool
{
// 1
Person *p = [[[[Person alloc] init] autorelease] autorelease];
// 0
// [p release];
}
return 0;
}
void test()
{
@autoreleasepool
{ // {開始代表建立了釋放池
// autorelease方法會返回物件本身
//呼叫完autorelease方法後,物件的計數器不變
// autorelease會將物件放到一個自動釋放池中
// 當自動釋放池被銷燬時,會對池子裡面的所有物件做一次release操作
Person *p = [[[Person alloc] init] autorelease];
p.age = 10;
@autoreleasepool
{
// 1
Person *p2 = [[[Person alloc] init] autorelease];
p2.age = 10;
}
Person *p3 = [[[Personalloc]init]autorelease];
}// 結束代表銷燬釋放池
}
注:1>、系統自帶的方法裡面沒有包含alloc、new、copy,說明返回的物件都是autorelease
2>、開發中經常會提供類方法,快速建立一個autorelease
------ IOS培訓、android培訓、期待與您交流!-------