IOS的對(duì)象都繼承于NSObject, 該對(duì)象有一個(gè)方法:retainCount ,內(nèi)存引用計(jì)數(shù)尊剔。 引用計(jì)數(shù)在很多技術(shù)都用到: window下的COM組件爪幻,多線程的信號(hào)量,讀寫(xiě)鎖须误,思想都一樣挨稿。
(一般情況下: 后面會(huì)討論例外情況)
alloc 對(duì)象分配后引用計(jì)數(shù)為1
retain 對(duì)象的引用計(jì)數(shù)+1
copy copy 一個(gè)對(duì)象變成新的對(duì)象(新內(nèi)存地址) 引用計(jì)數(shù)為1 原來(lái)對(duì)象計(jì)數(shù)不變
release 對(duì)象引用計(jì)數(shù)-1 如果為0釋放內(nèi)存
autorelease 對(duì)象引用計(jì)數(shù)-1 如果為0不馬上釋放,最近一個(gè)個(gè)pool時(shí)釋放
NSLog(@"sMessage retainCount:%u",[sMessage retainCount]);
內(nèi)存管理的原則就是最終的引用計(jì)數(shù)要平衡,
如果最后引用計(jì)數(shù)大于0 則會(huì)內(nèi)存泄露
如果引用 計(jì)數(shù)等于0還對(duì)該對(duì)象進(jìn)行操作,則會(huì)出現(xiàn)內(nèi)存訪問(wèn)失敗裤唠,crash 所以盡量設(shè)置為nil
這兩個(gè)問(wèn)題都很?chē)?yán)重,所以請(qǐng)一定注意內(nèi)存釋放和不用過(guò)后設(shè)置為nil
成員變量與屬性
實(shí)際情況并非上面那么簡(jiǎn)單臭家,你可能需要在一個(gè)函數(shù)里調(diào)用另一個(gè)函數(shù)分配的變量這時(shí)候
有兩個(gè)選擇: 類(lèi)成員變量和使用屬性
@interface TestMem: NSObject {
TestObject *m_testObject ; // 成員變量
TestObject *testObject; //成員變量
}
成員變量與上面的內(nèi)存管理是一致的疲陕,只是在不同的函數(shù)里要保持引用計(jì)數(shù)加減的平衡
所以要你要每次分配的時(shí)候檢查是否上次已經(jīng)分配了。是否還能調(diào)用
什么時(shí)候用屬性?
1. 把成員做為public.
2. outlet 一般聲明為屬性( 這個(gè)內(nèi)存于系統(tǒng)控制钉赁,但我們還是應(yīng)該做一樣操作蹄殃,后面會(huì)講)
3. 如果很多函數(shù)都需要改變這個(gè)對(duì)象 ,或這個(gè)函數(shù)會(huì)觸發(fā)很多次你踩,建議使用屬性诅岩。我們看看屬性函數(shù)展開(kāi)后是什么樣子:
// assign
-(void)setTestObject :(id)newValue{
testObject= newValue;
}
// retain
-(void)setTestObject :(id)newValue{
if (testObject!= newValue) {
[testObject release];
testObject= [newValue retain];
}
}
// copy
-(void)setTestObject :(id)newValue{
if (testObject != newValue) {
[testObject release];
testObject = [newValue copy];
}
}
asssign 相于于指針賦值,不對(duì)引用計(jì)數(shù)進(jìn)行操作姓蜂,注意原對(duì)象不用了按厘,一定要把這個(gè)設(shè)置為nil
retain 相當(dāng)于對(duì)原對(duì)象的引用計(jì)數(shù)加1
copy 不對(duì)原對(duì)象的引用計(jì)數(shù)改變,生成一個(gè)新對(duì)象引用計(jì)數(shù)為1
注意:
self.testObject 左值調(diào)用的是setTestObject 方法. 右值為get方法钱慢,get 方法比較簡(jiǎn)單不用說(shuō)了
而 真接testObject 使用的是成員變量
self.testObject = [[testObject alloc] init]; // 錯(cuò) reatin 兩次
testObject = [NSArray objectbyindex:0]; //錯(cuò) 不安全,沒(méi)有retain 后面release會(huì)出錯(cuò)
如果testObject已有值也會(huì)mem leak
自動(dòng)管理對(duì)象
IOS 提供了很多static(+) 創(chuàng)建對(duì)象的類(lèi)方法卿堂,這些方面是靜態(tài)的束莫,可以直接用類(lèi)名
調(diào)用如:
NSString *testString = [NSString stringWithFormat:@"test" ];
testString 是自動(dòng)管理的對(duì)象,你不用relese 他草描,他有一個(gè)很大的retain count, release后數(shù)字不變览绿。
5.?例外
有一些通過(guò)alloc 生成的對(duì)象相同是自動(dòng)管理的如:
NSString *testString = [[NSString alloc] initWithString:@"test1"];
retain count 同樣是很大的數(shù),沒(méi)辦法release
但為了代碼對(duì)應(yīng)穗慕,還是應(yīng)該加上[ testString release];
不然xcode的Analyze 會(huì)認(rèn)識(shí)內(nèi)存leak, 但I(xiàn)nstruments leak 工具檢測(cè)是沒(méi)有的
自動(dòng)管理對(duì)象
IOS 提供了很多static(+) 創(chuàng)建對(duì)象的類(lèi)方法饿敲,這些方面是靜態(tài)的,可以直接用類(lèi)名
調(diào)用如:
NSString *testString = [NSString stringWithFormat:@"test" ];
testString 是自動(dòng)管理的對(duì)象逛绵,你不用relese 他怀各,他有一個(gè)很大的retain count, release后數(shù)字不變。
5.?例外
有一些通過(guò)alloc 生成的對(duì)象相同是自動(dòng)管理的如:
NSString *testString = [[NSString alloc] initWithString:@"test1"];
retain count 同樣是很大的數(shù)术浪,沒(méi)辦法release
但為了代碼對(duì)應(yīng)瓢对,還是應(yīng)該加上[ testString release];
不然xcode的Analyze 會(huì)認(rèn)識(shí)內(nèi)存leak, 但I(xiàn)nstruments leak 工具檢測(cè)是沒(méi)有的
IOS內(nèi)存管理詳解
copy?和 retain 的區(qū)別
copy: 建立一個(gè)索引計(jì)數(shù)為1的對(duì)象,然后釋放舊對(duì)象
retain:釋放舊的對(duì)象胰苏,將舊對(duì)象的值賦予輸入對(duì)象硕蛹,再提高輸入對(duì)象的索引計(jì)數(shù)為1
那上面的是什么該死的意思呢?
Copy其實(shí)是建立了一個(gè)相同的對(duì)象硕并,而retain不是:
比如一個(gè)NSString對(duì)象法焰,地址為0×1111,內(nèi)容為@”STR”
Copy到另外一個(gè)NSString之后倔毙,地址為0×2222埃仪,內(nèi)容相同,新的對(duì)象retain為1普监,舊有對(duì)象沒(méi)有變化
retain到另外一個(gè)NSString之后贵试,地址相同(建立一個(gè)指針琉兜,指針拷貝),內(nèi)容當(dāng)然相同毙玻,這個(gè)對(duì)象的retain值+1
也就是說(shuō)豌蟋,retain是指針拷貝,copy是內(nèi)容拷貝桑滩。哇梧疲,比想象的簡(jiǎn)單多了…
誤釋放對(duì)象
問(wèn)題一:
1.value = [array objectAtIndex:n]; //得到一個(gè)數(shù)組中的對(duì)象
2.[arry removeObjectAtIndex:n]; //卸載那個(gè)對(duì)象
?????value = [array objectAtIndex:n]; //得到一個(gè)數(shù)組中的對(duì)象
[arry removeObjectAtIndex:n]; //卸載那個(gè)對(duì)象
因?yàn)関alue得到了那個(gè)對(duì)象,但是由于另外一個(gè)擁有者release了該對(duì)象运准,所以其實(shí)value現(xiàn)在成了搖擺指針(無(wú)效數(shù)據(jù))
問(wèn)題二:
1.myArray = [NSArray array];
2....
3.[myArray release];
?????myArray = [NSArray array];
...
[myArray release];
NSArray返回的是一個(gè)自動(dòng)釋放對(duì)象幌氮,不僅myArray不應(yīng)該在一段時(shí)間后release,而應(yīng)該在適當(dāng)?shù)臅r(shí)候先retain胁澳,以防止該array被系統(tǒng)誤釋放该互。
問(wèn)題三:
1.rocket = [rocketLauncher aRocket];
2.[rocketLauncher release];
?????rocket = [rocketLauncher aRocket];
[rocketLauncher release];
和array這種數(shù)據(jù)收集類(lèi)對(duì)象一樣,如果我們得到了一個(gè)類(lèi)的子對(duì)象而不retain它韭畸,那么在原父類(lèi)被釋放的時(shí)候宇智,這個(gè)rocket其實(shí)也會(huì)失去其意義。
Cocoa不同內(nèi)存管理環(huán)境下的autorelease
H 混合內(nèi)存管理環(huán)境:垃圾收集法(Garbage Collection)+索引計(jì)數(shù)法(Reference Counting)
雖然大多數(shù)情況下混合環(huán)境是不被推薦的胰丁,但是如果在這個(gè)情況下随橘,autorelease需要注意以下事項(xiàng):
垃圾收集混合環(huán)境下:應(yīng)該使用drain方法,因?yàn)閞elease在GC模式下沒(méi)有意義
索引計(jì)數(shù)環(huán)境下:drain和release對(duì)于autoreleasepool(自動(dòng)釋放池)的效果相同
對(duì)autorelease的誤解
A Cocoa的內(nèi)存管理分為 索引計(jì)數(shù)法(Reference Counting/ Retain Count)和 垃圾收集法(Garbage Collection)锦庸。而iPhone上目前只支持前者机蔗,所以autorelease就成為很多人的“捷徑”。
但是甘萧!autorelease其實(shí)并不是“自動(dòng)釋放”萝嘁,不像垃圾收集法,對(duì)對(duì)象之間的關(guān)系偵測(cè)后發(fā)現(xiàn)垃圾-刪除幔嗦。但是autorelease其實(shí)是“延后釋放”酿愧,在一個(gè)運(yùn)行周期后被標(biāo)記為autorelease會(huì)被釋放掉。
切記小心使用autorelease邀泉,理解autorelease嬉挡,防止在你還需要該對(duì)象的時(shí)候已經(jīng)被系統(tǒng)釋放掉了。
Interface Builder參與的內(nèi)存管理問(wèn)題
要點(diǎn):
如果一個(gè)變量在類(lèi)中被定義為了 IBOutlet 那么你無(wú)需對(duì)其進(jìn)行實(shí)例化汇恤,xib載入器會(huì)對(duì)其初始化庞钢。
如果一個(gè)變量在類(lèi)中被定義為了 IBOutlet 那么你必須負(fù)責(zé)將其釋放。xib載入器不會(huì)幫忙的… …
*切不要初始化兩回因谎,內(nèi)存會(huì)溢出基括,而且對(duì)象鎖定也會(huì)出錯(cuò)。
關(guān)于索引計(jì)數(shù)(Reference Counting)的問(wèn)題
1.*retain值 = 索引計(jì)數(shù)//(Reference Counting)
???*retain值 = 索引計(jì)數(shù)//(Reference Counting)
NSArray對(duì)象會(huì)retain(retain值加一)任何數(shù)組中的對(duì)象财岔。當(dāng)NSArray被卸載(dealloc)的時(shí)候风皿,所有數(shù)組中的對(duì)象會(huì)被執(zhí)行一次釋放(retain值減一)河爹。不僅僅是NSArray,任何收集類(lèi)(Collection Classes)都執(zhí)行類(lèi)似操作桐款。例如NSDictionary咸这,甚至UINavigationController。
Alloc/init建立的對(duì)象魔眨,索引計(jì)數(shù)為1媳维。無(wú)需將其再次retain。
[NSArray array]和[NSDate date]等“方法”建立一個(gè)索引計(jì)數(shù)為1的對(duì)象遏暴,但是也是一個(gè)自動(dòng)釋放對(duì)象侄刽。所以是本地臨時(shí)對(duì)象,那么無(wú)所謂了朋凉。如果是打算在全Class中使用的變量(iVar)州丹,則必須retain它。
缺省的類(lèi)方法返回值都被執(zhí)行了“自動(dòng)釋放”方法侥啤。(*如上中的NSArray)
在類(lèi)中的卸載方法“dealloc”中当叭,release所有未被平衡的NS對(duì)象。(*所有未被autorelease盖灸,而retain值為1的)
NSString的內(nèi)存管理
如下實(shí)例:
1.aString = @"I am a string that 2 years old, man!";
aString = @"I am a string that 2 years old, man!";
這種情況下,字符串儲(chǔ)存和管理由系統(tǒng)做磺芭,我們不用操心赁炎。
1.aString = [NSString stringWithFormat:@"I am a string that %d years old, man!",2];
aString = [NSString stringWithFormat:@"I am a string that %d years old, man!",2];
第二種情況下,我們需要去retain和release這個(gè)字符串钾腺,系統(tǒng)不管徙垫。
Objective-C內(nèi)存管理
1,你初始化(alloc/init)的對(duì)象放棒,你需要釋放(release)它姻报。例如:
1.NSMutableArray aArray = [[NSArray alloc] init];
NSMutableArray aArray = [[NSArray alloc] init];
后,需要
1.[aArray release];
[aArray release];
2间螟,你retain或copy的吴旋,你需要釋放它。例如:
1.[aArray retain]
[aArray retain]
后厢破,需要
1.[aArray release];
[aArray release];
3荣瑟,被傳遞(assign)的對(duì)象,你需要斟酌的retain和release摩泪。例如:
1.obj2 = [[obj1 someMethod] autorelease];
obj2 = [[obj1 someMethod] autorelease];
對(duì)象2接收對(duì)象1的一個(gè)自動(dòng)釋放的值笆焰,或傳遞一個(gè)基本數(shù)據(jù)類(lèi)型(NSInteger,NSString)時(shí): 你或希望將對(duì)象2進(jìn)行retain见坑,以防止它在被使用之前就被自動(dòng)釋放掉嚷掠。但是在retain后捏检,一定要在適當(dāng)?shù)臅r(shí)候進(jìn)行釋放。
為什么不能直接調(diào)用dealloc而是release
dealloc不等于C中的free不皆,dealloc并不將內(nèi)存釋放贯城,也不會(huì)將索引計(jì)數(shù)(Reference counting)降低。于是直接調(diào)用dealloc反而無(wú)法釋放內(nèi)存粟焊。
在Objective-C中冤狡,索引計(jì)數(shù)是起決定性作用的。
strong 和weak
iOS 5 中對(duì)屬性的設(shè)置新增了strong 和weak關(guān)鍵字來(lái)修飾屬性(iOS 5 之前不支持ARC)
strong 用來(lái)修飾強(qiáng)引用的屬性项棠;
@property (strong) SomeClass * aObject;
對(duì)應(yīng)原來(lái)的
@property (retain) SomeClass * aObject; 和 @property (copy) SomeClass * aObject;
weak 用來(lái)修飾弱引用的屬性悲雳;
@property (weak) SomeClass * aObject;
對(duì)應(yīng)原來(lái)的
@property (assign) SomeClass * aObject;