你好,歡迎來到IOS教程網

 Ios教程網 >> IOS訊息 >> 關於IOS >> IOS開發(40)之objective

IOS開發(40)之objective

編輯:關於IOS

obj-c本質就是"改進過的c語言",大家都知道c語言是沒有垃圾回收(GC)機制的(注:雖然obj-c2.0後來增加了GC功能,但是在iphone上不能用,因此對於iOS平台的程序員來講,這個幾乎沒啥用),所以在obj-c中寫程序時,對於資源的釋放得由開發人員手動處理,相對要費心一些。

引用計數

這是一種古老但有效的內存管理方式。每個對象(特指:類的實例)內部都有一個retainCount的引用計數,對象剛被創建時,retainCount為1,可以手動調用retain方法使retainCount+1,同樣也可以手動調用release方法使retainCount-1,調用release方法時,如果retainCount值減到0,系統將自動調用對象的dealloc方法(類似於c#中的dispose方法),開發人員可以在dealloc中釋放或清理資源。

1、基本用法

為了演示這種基本方式,先定義一個類Sample

類接口部分Sample.h

//
//  Sample.h
//  MemoryManage_1
//
//  Created by jimmy.yang on 11-2-19.
//  Copyright 2011 __MyCompanyName__. All rights reserved.
//

#import <Foundation/Foundation.h>

@interface Sample : NSObject {

}

@end
類實現部分Sample.m

//
//  Sample.m
//  MemoryManage_1
//
//  Created by jimmy.yang on 11-2-19.
//  Copyright 2011 __MyCompanyName__. All rights reserved.
//

#import "Sample.h"

@implementation Sample

-(id) init
{
 if (self=[super init]){
  NSLog(@"構造函數被調用了!當前引用計數:%d",[self retainCount]);
 }
 return (self);
}

-(void) dealloc{
 NSLog(@"析構函數將要執行...,當前引用計數:%d",[self retainCount]);
 [super dealloc];
}
@end
代碼很簡單,除了"構造函數"跟"析構函數"之外,沒有任何其它多余處理。

主程序調用

#import <Foundation/Foundation.h>
#import "Sample.h"

int main (int argc, const char * argv[]) { 
 
 Sample *_sample = [Sample new]; //構造函數被調用了!當前引用計數:1
 NSLog(@"_sample.retainCount=%d",[_sample retainCount]);//1 
 
 [_sample retain];
 NSLog(@"_sample.retainCount=%d",[_sample retainCount]);//2
 
 [_sample retain];
 NSLog(@"_sample.retainCount=%d",[_sample retainCount]);//3 
 
 [_sample release];
 NSLog(@"_sample.retainCount=%d",[_sample retainCount]);//2
 
 [_sample release];
 NSLog(@"_sample.retainCount=%d",[_sample retainCount]);//1
 
 [_sample release];//析構函數將要執行...,當前引用計數:1
 NSLog(@"_sample.retainCount=%d",[_sample retainCount]);//1,注:即便是在析構函數執行後,如果立即再次引用對象的retainCount,仍然返回1,但以後不管再試圖引用該對象的任何屬性或方法,都將報錯
 NSLog(@"_sample.retainCount=%d",[_sample retainCount]);//對象被釋放之後,如果再嘗試引用該對象的任何其它方法,則報錯
 //[_sample retain];//同上,會報錯 
 
 return 0; 
}
這段代碼主要驗證:對象剛創建時retainCount是否為1,以及retain和release是否可以改變retainCount的值,同時retainCount減到0時,是否會自動執行dealloc函數

nil 的問題:

1.1 如果僅聲明一個Sample類型的變量(其實就是一個指針),而不實例化,其初始值為nil

1.2 變量實例化以後,就算release掉,dealloc被成功調用,其retainCount並不馬上回到0(還能立即調用一次且僅一次[xxx retainCount]),而且指針變量本身也不會自動歸為nil值

1.3 dealloc被調用後,必須手動賦值nil,retainCount才會自動歸0

以上結論是實際試驗得出來的,見下面的代碼:

 Sample *s ; 
 NSLog(@"s %@,retainCount=%d",s==nil?@"is nil":@"is not nil",[s retainCount]);//s is nil,retainCount=0 
 s = [Sample new];
 NSLog(@"s %@,retainCount=%d",s==nil?@"is nil":@"is not nil",[s retainCount]);//s is not nil,retainCount=1 
 [s release];
 NSLog(@"s %@,retainCount=%d",s==nil?@"is nil":@"is not nil",[s retainCount]);//s is not nil,retainCount=1
 //NSLog(@"s %@,retainCount=%d",s==nil?@"is nil":@"is not nil",[s retainCount]);//報錯:Program received signal:  “EXC_BAD_ACCESS”.
 s = nil;
 NSLog(@"s %@,retainCount=%d",s==nil?@"is nil":@"is not nil",[s retainCount]);//s is nil,retainCount=0
所以千萬別用if (x == nil) 或 if ([x retainCount]==0)來判斷對象是否被銷毀,除非你每次銷毀對象後,手動顯式將其賦值為nil

2、復雜情況

上面的示例過於簡章,只有一個類自己獨耍,如果有多個類,且相互之間有聯系時,情況要復雜一些。下面我們設計二個類Shoe和Man(即“鞋子類”和”人“),每個人都要穿鞋,所以Man與Shoe之間應該是Man擁有Shoe的關系。

Shoe.h接口定義部分

#import <Foundation/Foundation.h>

@interface Shoe : NSObject {
 NSString* _shoeColor;
 int _shoeSize;
}

//鞋子尺寸
-(void) setSize:(int) size;
-(int) Size;

//鞋子顏色
-(void) setColor:(NSString*) color;
-(NSString*) Color;

//設置鞋子的顏色和尺碼
-(void) setColorAndSize:(NSString*) pColor shoeSize:(int) pSize;

@end
Shoe.m實現部分

//
//  Shoe.m
//  MemoryManage_1
//
//  Created by jimmy.yang on 11-2-19.
//  Copyright 2011 __MyCompanyName__. All rights reserved.
//

#import "Shoe.h"

@implementation Shoe

//構造函數
-(id)init
{
 if (self=[super init]){
  _shoeColor = @"black";
  _shoeSize = 35;
 }
 NSLog(@"一雙 %@ %d碼 的鞋子造好了!",_shoeColor,_shoeSize);
 return (self);
}

-(void) setColor:(NSString *) newColor
{
 _shoeColor = newColor;
}

-(NSString*) Color
{
 return _shoeColor;
}

-(void) setSize:(int) newSize
{
 _shoeSize = newSize;

-(int) Size
{
 return _shoeSize;
}

-(void) setColorAndSize:(NSString *)color shoeSize:(int)size
{
 [self setColor:color];
 [self setSize:size];
}

//析構函數
-(void) dealloc
{
 NSLog(@"%@ %d碼的鞋子正在被人道毀滅!",_shoeColor,_shoeSize);
 [super dealloc];
}

@end
Man.h定義部分

//
//  Man.h
//  MemoryManage_1
//
//  Created by jimmy.yang on 11-2-20.
//  Copyright 2011 __MyCompanyName__. All rights reserved.
//

#import <Foundation/Foundation.h>
#import "Shoe.h"

@interface Man : NSObject {
 NSString *_name;
 Shoe *_shoe;
}

-(void) setName:(NSString*) name;
-(NSString*)Name;

-(void) wearShoe:(Shoe*) shoe;
@end
Man.m實現部分

//
//  Man.m
//  MemoryManage_1
//
//  Created by jimmy.yang on 11-2-20.
//  Copyright 2011 __MyCompanyName__. All rights reserved.
//

#import "Man.h"

@implementation Man

//構造函數
-(id)init
{
 if (self=[super init]){
  _name = @"no name";
 }
 NSLog(@"新人/"%@/"出生了!",_name);
 return (self);
}

-(void) setName:(NSString *)newName
{
 _name =newName;
}

-(NSString*)Name
{
 return _name;
}

-(void) wearShoe:(Shoe *)shoe
{
 _shoe = shoe;
}

//析構函數
-(void) dealloc
{
 NSLog(@"/"%@/"要死了! ",_name);
 [super dealloc];
}
 
@end
main函數調用

#import <Foundation/Foundation.h>
#import "Shoe.h"
#import "Man.h"

int main (int argc, const char * argv[]) { 
 
 Man *jimmy = [Man new];
 [jimmy setName:@"Jimmy"];
 
 
 Shoe *black40 =[Shoe new];
 [black40 setColorAndSize:@"Black" shoeSize:40];
 
 [jimmy wearShoe:black40];
 
 [jimmy release];
 [black40 release];
 
 return 0;
   
}
2011-02-23 13:05:50.550 MemoryManage[253:a0f] 新人"no name"出生了!
2011-02-23 13:05:50.560 MemoryManage[253:a0f] 一雙 black 35碼 的鞋子造好了!
2011-02-23 13:05:50.634 MemoryManage[253:a0f] "Jimmy"要死了!
2011-02-23 13:05:50.636 MemoryManage[253:a0f] Black 40碼的鞋子正在被人道毀滅!

以上是輸出結果,一切正常,jimmy與black40占用的資源最終都得到了釋放。但是有一點不太合理,既然鞋子(black40)是屬於人(jimmy)的,為什麼人死了(即:[jimmy release]),卻還要main函數來責任燒掉他的鞋子?(即:main函數中還是單獨寫一行[black40 release]) 貌似人死的時候,就連帶自上的所有東西一並帶走,這樣更方便吧。

ok,我們來改造一下Man.m中的dealloc()方法,改成下面這樣:

//析構函數
-(void) dealloc
{
 NSLog(@"/"%@/"要死了! ",_name);
 [_shoe release];//這裡釋放_shoe
 [super dealloc];
}
即:在Man被銷毀的時候,先把_shoe給銷毀。這樣在main()函數中,就不再需要單獨寫一行[black40 release]來釋放black40了.

現在又有新情況了:jimmy交了一個好朋友mike,二人成了鐵哥們,然後jimmy決定把自己的鞋子black40,跟mike共同擁有,於是main函數就成了下面這樣:

int main (int argc, const char * argv[]) { 
 
 Man *jimmy = [Man new];
 [jimmy setName:@"Jimmy"]; 
 
 Shoe *black40 =[Shoe new];
 [black40 setColorAndSize:@"Black" shoeSize:40];
 
 [jimmy wearShoe:black40];
 
 Man *mike = [Man new];
 [mike setName:@"mike"];
 [mike wearShoe:black40];//mike跟jimmy,現在共同擁有一雙40碼黑色的鞋子
 
 [jimmy release];
 [mike release]; 
 
    return 0;
}
麻煩來了:jimmy在掛掉的時候(即[jimmy release]這一行),已經順手把自己的鞋子也給銷毀了(也許他忘記了mike也在穿它),然後mike在死的時候,准備燒掉自已的鞋子black40,卻被告之該對象已經不存在了。於是程序運行報錯:

Running…
2011-02-23 13:38:53.169 MemoryManage[374:a0f] 新人"no name"出生了!
2011-02-23 13:38:53.176 MemoryManage[374:a0f] 一雙 black 35碼 的鞋子造好了!
2011-02-23 13:38:53.177 MemoryManage[374:a0f] 新人"no name"出生了!
2011-02-23 13:38:53.179 MemoryManage[374:a0f] "Jimmy"要死了!
2011-02-23 13:38:53.181 MemoryManage[374:a0f] Black 40碼的鞋子正在被人道毀滅!
2011-02-23 13:38:53.183 MemoryManage[374:a0f] "mike"要死了!
Program received signal:  “EXC_BAD_ACCESS”.
sharedlibrary apply-load-rules all
(gdb)

上面紅色的部分表示程序出錯了:Bad_Access也就是說訪問不存在的地址。

最解決的辦法莫過於又回到原點,Man.m的dealloc中不連帶釋放Shoe實例,然後把共用的鞋子放到main函數中,等所有人都掛掉後,最後再銷毀Shoe實例,但是估計main()函數會有意見了:你們二個都死了,還要來麻煩我料理後事。

舉這個例子無非就是得出這樣一個原則:對於new出來的對象,使用retain造成的影響一定要運用相應的release抵消掉,反之亦然,否則,要麼對象不會被銷毀,要麼過早銷毀導致後面的非法引用而出錯。

 

  1. 上一頁:
  2. 下一頁:
蘋果刷機越獄教程| IOS教程問題解答| IOS技巧綜合| IOS7技巧| IOS8教程
Copyright © Ios教程網 All Rights Reserved