/*今天本來在研究tableView:moveRowAtIndexPath:toIndexPath:這個方法,但是一個crash,讓我有了一些有意思的發現,從而讓我對內存管理有了更深的認識,不過這些只是我的個人理解,或許不正確,希望大家看過以後也可以發表一下意見*/
首先我就直接上一段代碼
- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath *)destinationIndexPath
{
NSInteger fromRow = [sourceIndexPath row];
NSInteger toRow = [destinationIndexPath row];
id obj = [self.mArr objectAtIndex:fromRow];
//NSLog(@"%d,%p",[obj retainCount],obj);--------------1
//NSLog(@"%p",[self.mArr objectAtIndex:fromRow]);-----2
//[obj retain];---------------------------------------3
//NSLog(@"%d",[obj retainCount]);---------------------4
[self.mArr removeObjectAtIndex:fromRow];
//NSLog(@"%d",[obj retainCount]);---------------------5
//NSLog(@"%d",[obj retainCount]);---------------------6
//NSLog(@"%p",obj);-----------------------------------7
if (destinationIndexPath.row > [self.mArr count])
{
[self.mArr addObject:obj];
//NSLog(@"%d",[obj retainCount]);-----------------8
}
else
{
//NSLog(@"%d",[obj retainCount]);-----------------9
[self.mArr insertObject:obj atIndex:toRow];
//NSLog(@"%d,%p",[obj retainCount],obj);---------10
}
//[self.mArr insertObject:obj atIndex:toRow];--------11
//[obj release];-------------------------------------12
//NSLog(@"%d",[obj retainCount]);--------------------13
}
將上面代碼的3與12打開則不會發生崩潰。
其他是暴力調試用的,可以嘗試打開它們,自己調試,會發現有意思的現象。在這裏我簡單說一下我的發現。
首先,將第1句與第2句打開,從打印結果你會發現,obj與[self.mArr objectAtIndex:fromRow]是同一個對象。
然後只打開1,5,10並且保持兩個retain,release語句關閉時,你會發現打印的引用計數的結果爲1,1,2。
但是,如果打開兩個retain,release語句,並且打開它們相應的引用計數語句,也就是打開1,3,4,5,10,12,13;這時打印的引用計數的結果爲1,2,1,2,1。
好玩的事情發生了,就是在經過[self.mArr removeObjectAtIndex:fromRow]語句後,第一種情況的引用計數並沒有減少,仍是1,而第二種情況的引用計數卻由2降爲1,我就很納悶,這是什麼情況,所以添加了語句9,因爲我懷疑可能只是當時引用計數並未減少,而是在之後又進行了減少,這時出現了預料中的結果,在打斷點的情況下,清楚地看到程序是在執行第9句的時候崩潰了,也就是說在這裏obj的引用計數已經變爲0,被清除了,所以纔會引發crash,但是爲什麼不在第5句的時候就崩潰呢?爲了排除其他想不到的因素,我在語句5後緊跟着添加了語句6,運行,哎?這次崩在了第6句,於是我大膽猜測,蘋果可能由於某種原因設計了一種機制,就是在對象被某種方式使引用計數降爲0以後,仍暫時保留它,直到下次調用以後,再清除。不過,這些只是我在自己現有的水平下的臆測,也希望知道其中原委的人解決我的疑惑。(補充:當我將第1句引去,則不會在第6句發生崩潰,第9句也不會崩潰,只在第二次移動同一句時,產生崩潰,這推翻了我的猜測,也加深了我的疑惑)。
沒移動前移動一次再次移動同一標號行產生了崩潰。
這個事情讓我重新審視了內存管理的法則。附在最後
1,當你使用new、alloc、或copy方法創建一個對象時,該對象的保留計數器值爲1.當不再使用該對象時,你要負責向該對象發送一條release或autorelease消息。這樣,該對象將在其使用壽命結束時被銷燬。
2當你通過任何其他方法獲得一個對象時,則假設該對象的保留計數器值爲1,而且已經被設置爲自動釋放,你不需要執行任何操作來確保該對象被清理。如果你打算在一段時間內擁有該對象,則需要保留它並確保在操作完成時釋放它。
3如果你保留了某個對象,你需要(最終)釋放或自動釋放該對象。必須保持retain方法和release方法的使用次數相等(法則引自小橘子書)