2009-09-20 5 views
1

J'ai une méthode dans un UIViewController de mon application iPhone (à l'intérieur d'un UINavigationController) qui est appelée chaque fois qu'une ligne est sélectionnée dans la table dans la vue de ViewController. Dans cette méthode, j'accède au tableau de "Dream" stocké dans un champ d'instance dreamsArray, qui contient NSManagedObjects de ma base de données. Je peux accéder aux objets de ce tableau dans d'autres méthodes, mais il semble que chaque fois que j'essaie de récupérer ou de modifier des objets récupérés à partir de ce tableau dans cette méthode particulière, le programme se bloque.L'application se bloque lors de l'accès à NSManagedObjects dans une certaine méthode

Voici comment dreamsArray est créé:

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]; 

Une instance dans laquelle l'interrogation dreamsArray et ses objets fonctionne:

- (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; 
} 

Et la méthode qui a tous les problèmes:

- (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]; 
} 

L'application se bloque immédiatement après l'appel de dreamsArray.

Même l'appel d'un NSLog(@"%@", dream.title) simple dans cette méthode provoque un blocage. Qu'est-ce qui pourrait mal tourner ici?

Répondre

27

Le code suivant est plus court, plus efficace, plus facile à lire, et ne pas les six ou si des fuites de mémoire de votre premier bloc:

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]; 

Cette méthode est réécrite pour être plus petite et non plus rêve -release, qui se traduit par Crashers:

- (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; 
} 

Cette méthode a eu un sur la libération de « rêve » et une fuite sur un NSArray et donc une instance « rêve », aussi bien.

- (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]; 
} 

Ce n'est pas lactosérum clair la variable d'instance sur EditDreamController est singulier quand il prend un tableau - il devrait être « rêves » si vous pouvez vraiment définir plusieurs d'entre eux.

-Wil

+0

Point secondaire: J'utiliserais personnellement [[fetchResults mutableCopy] autorelease] plutôt que [NSMutableArray arrayWithArray: fetchResults] car, en théorie, fetchResults pourrait avoir une méthode mutableCopy optimisée (à l'avenir, même si ce n'est pas le cas actuellement). –

+1

Je n'utilise jamais -mutableCopy, car je ne lui fais pas confiance. Une raison est que je ne peux presque jamais me rappeler si 'mutableCopy' est profond ou pas (et ne devrait pas l'être), et si * I * ne me souviens pas, comment puis-je m'attendre à ce que d'autres personnes lisent mon code? Un autre est parce que s'il a été optimisé de cette manière, l'utiliser pour sécuriser l'énumération rapide (parce que je modifie le tableau de base) serait encore dangereux. En outre, si le tableau de base était une sous-classe de NSArray foutue, maintenant je sais que c'est juste un normal. –

+1

L'argument selon lequel vous ne pouvez pas vous souvenir si c'est une copie profonde est faible, car vous pouvez en dire autant de arrayWithArray. Cela ne veut rien enlever à votre point plus général: si vous ne pouvez pas facilement vous rappeler quelque chose, vous ne devriez pas le faire (par exemple, j'oublie l'ordre de priorité entre && et || et j'utilise donc des parenthèses). Personnellement, je ne pense pas que se rappeler si mutableCopy fait une copie profonde tombe dans cette catégorie; c'est quelque chose que tu devrais savoir. –

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

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

[dream release]; 

Vous ne devriez pas diffuser -dream. Le tableau en a une emprise. Idem pour la méthode -tableView:didSelectRowAtIndexPath:. Très probablement, l'objet a été libéré suffisamment de fois pour être désalloué, laissant derrière lui une référence pendante dans le tableau.

Résultat final?

Un accident.

En outre, votre premier bit de code a:

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]; 

Il y a plusieurs morceaux de code confus ici.

(1) Pourquoi définissez-vous dreamsArray comme un tableau mutable vide, puis réinitialisez-le pour faire référence à une copie mutable des résultats de la requête d'extraction? Vous fuyez le tableau mutable vide.

(2) Vous appelez [self managedObjectContext], mais ne faites rien avec la valeur de retour. Ensuite, vous utilisez directement managedObjectContext. Il suffit d'utiliser [self managedObjectContext] partout. Les frais généraux sont négligeables. (3) Vous créez une demande de récupération conservée et l'attribuez à request, mais jamais la libérez. Une autre fuite de mémoire.

(4) Pourquoi copiez-vous le mutableFetchResults deux fois? Cela n'a aucun sens (et conduit à une autre fuite). Dans l'ensemble, je suggère de revoir la documentation sur la gestion de la mémoire Objective-C.

+0

Oui, je le pense maintenant aussi. Ce fut ma première aventure dans la gestion de la mémoire .. bien que le code de récupération de données a été tiré d'un tutoriel. :/ –

Questions connexes