2009-09-20 60 views
1

我的iPhone應用程序(在UINavigationController中)的UIViewController中有一個方法,只要在ViewController的視圖中的表中選擇一行即可調用該方法。在此方法中,我訪問存儲在實例字段dreamsArray中的「Dream」數組,該數組包含來自我的數據庫的NSManagedObjects。我可以在其他方法中訪問此數組中的對象,但似乎每當我嘗試從此數組中檢索或修改檢索到的對象時,程序都會崩潰。每當在某個方法中訪問NSManagedObjects時,應用程序崩潰

這裏是dreamsArray是如何創建的:

dreamsArray = [[NSMutableArray alloc] init]; 

    [self managedObjectContext]; 

    NSFetchRequest *request = [[NSFetchRequest alloc] init]; 
    NSEntityDescription *entity = [NSEntityDescription entityForName:@"Dream" inManagedObjectContext:managedObjectContext]; 
    [request setEntity:entity]; 

    NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"title" ascending:NO]; 
    NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil]; 
    [request setSortDescriptors:sortDescriptors]; 
    [sortDescriptors release]; [sortDescriptor release]; 

    NSError *error; 
    NSMutableArray *mutableFetchResults = [[managedObjectContext executeFetchRequest:request error:&error] mutableCopy]; 
    if (mutableFetchResults == nil) 
     NSLog(@"oh noes! no fetch results DreamsTabController:45"); 

    dreamsArray = [mutableFetchResults mutableCopy]; 
    [mutableFetchResults release]; 
    [request release]; 

的實例中查詢dreamsArray及其對象的工作原理:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { 
    static NSString *cellID = @"Cell"; 

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellID]; 
    if (cell == nil) 
     cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:cellID] autorelease]; 

    Dream *dream = (Dream *)[dreamsArray objectAtIndex:indexPath.row]; 

    cell.textLabel.text = [dream title]; 
    cell.detailTextLabel.text = @"foo!"; 

    [dream release]; 

    return cell; 
} 

而且具有所有問題的方法:

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { 
    Dream *dream = (Dream *)[dreamsArray objectAtIndex:indexPath.row]; 
    // BOOM - crashes right here 
    EditDreamController *edit = [[EditDreamController alloc] initWithNibName:@"EditDream" bundle:nil]; 
    edit.dream = [[NSArray alloc] initWithObjects:dream.dreamContent, nil]; 
    [navigationController pushViewController:edit animated:YES]; 
    [dream release]; 
    [edit release]; 
} 

這個應用程序在dreamsArray被查詢後立即崩潰。

即使在此方法中調用簡單的NSLog(@"%@", dream.title)也會導致崩潰。這裏可能會出現什麼問題?

回答

27

下面的代碼更短,更高效,更易於閱讀,並沒有你的第一個塊的六個左右的內存泄漏:

NSFetchRequest *request = [[NSFetchRequest alloc] init]; 
[request setEntity:[NSEntityDescription entityForName:@"Dream" inManagedObjectContext:[self managedObjectContext]]]; 

static NSArray *sortDescriptors = nil; 
if (!sortDescriptors) 
    sortDescriptors = [[NSArray alloc] initWithObject:[[[NSSortDescriptor alloc] initWithKey:@"title" ascending:NO] autorelease]]; 
[request setSortDescriptors:sortDescriptors]; 

NSError *error = nil; 
NSArray *fetchResults = [managedObjectContext executeFetchRequest:request error:&error]; 
if (!fetchResults) 
    NSLog(@"oh noes! no fetch results DreamsTabController:45, error %@", error); 
[request release]; 

dreamsArray = [NSMutableArray arrayWithArray:fetchResults]; 

這種方法被改寫爲更小和沒有結束-release夢想,這導致不速之客:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath; 
{ 
    static NSString *cellID = @"Cell"; 

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellID] ? : [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:cellID] autorelease]; 

    Dream *dream = [dreamsArray objectAtIndex:indexPath.row]; 
    cell.textLabel.text = dream.title; 
    cell.detailTextLabel.text = @"foo!"; 

    return cell; 
} 

此方法有一個過放的「夢」和一個NSArray泄漏並且因此「夢」實例,以及。

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath; 
{ 
    EditDreamController *editDreamController = [[EditDreamController alloc] initWithNibName:@"EditDream" bundle:nil]; 

    Dream *dream = [dreamsArray objectAtIndex:indexPath.row]; 
    editDreamController.dream = [NSArray arrayWithObjects:dream.dreamContent]; 

    [navigationController pushViewController:editDreamController animated:YES]; 
    [editDreamController release]; 
} 

目前尚不清楚乳清上EditDreamController實例變量是奇異的,當它需要一個數組 - 它應該是「夢想」,如果你真的可以設置超過其中之一。

-Well

+0

小問題:我個人會使用[[fetchResults mutableCopy] autorelease]而不是[NSMutableArray arrayWithArray:fetchResults],因爲理論上fetchResults可以有一個優化的mutableCopy方法(將來,即使目前沒有)。 –

+1

我永遠不會使用-mutableCopy,因爲我只是不相信它。其中一個原因是,我幾乎不記得'mutableCopy'是否很深(不應該),如果我不記得,我怎麼能期待其他人閱讀我的代碼來記住? 另一個是因爲如果它以這種方式優化,那麼使用它來快速枚舉安全(因爲我正在修改基本數組)仍然是不安全的。 另外,如果基本數組是一個不可思議的NSArray子類,現在我知道這只是一個正常的。 –

+1

您不記得它是否是深層副本的說法很弱,因爲您可以對arrayWithArray說同樣的話。如果你不能輕易記住某些東西,你就不應該這樣做(例如,我忘記了&&和||之間的優先順序,所以我使用了括號)。我個人並不認爲記得mutableCopy是否有深度拷貝屬於這個類別;這是你應該知道的。 –

8
Dream *dream = (Dream *)[dreamsArray objectAtIndex:indexPath.row]; 

cell.textLabel.text = [dream title]; 
cell.detailTextLabel.text = @"foo!"; 

[dream release]; 

你不應該發佈-dream。陣列擁有它。 -tableView:didSelectRowAtIndexPath:方法也一樣。最有可能的是,該對象已經釋放了足夠多的時間以便被釋放,從而在數組中留下懸掛的引用。

最終結果?

崩潰。

另外,你的代碼的第一位具有:

dreamsArray = [[NSMutableArray alloc] init]; 

[self managedObjectContext]; 

NSFetchRequest *request = [[NSFetchRequest alloc] init]; 
NSEntityDescription *entity = [NSEntityDescription entityForName:@"Dream" inManagedObjectContext:managedObjectContext]; 
[request setEntity:entity]; 

NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"title" ascending:NO]; 
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil]; 
[request setSortDescriptors:sortDescriptors]; 
[sortDescriptors release]; [sortDescriptor release]; 

NSError *error; 
NSMutableArray *mutableFetchResults = [[managedObjectContext executeFetchRequest:request error:&error] mutableCopy]; 
if (mutableFetchResults == nil) 
    NSLog(@"oh noes! no fetch results DreamsTabController:45"); 

dreamsArray = [mutableFetchResults mutableCopy]; 

有迷茫代碼幾位在這裏。 (1)爲什麼你將dreamsArray設置成一個空的可變數組,然後將其重置爲引用獲取請求結果的可變副本?你正在泄漏空的可變陣列。

(2)您致電[self managedObjectContext],但不要對返回值做任何事情。然後您直接使用managedObjectContext。隨處可以使用[self managedObjectContext]。開銷可以忽略不計。 (3)您創建一個保留獲取請求並將其分配給request,但從不釋放它。另一個內存泄漏。

(4)爲什麼要複製mutableFetchResults兩次?這沒有任何意義(並導致另一次泄漏)。總而言之,我建議重新訪問Objective-C內存管理的文檔。

+0

是的,我現在也這麼認爲。這是我在內存管理中的第一次冒險..雖然數據獲取代碼是從教程中獲取的。 :/ –

相關問題