2012-03-10 1 views
1

Je travaille sur le Guide BNR de la programmation iOS, et j'ai du mal à essayer de résoudre un problème de division des données UITableView en sections. Le résumé de ce que j'ai fait est ceci. J'avais besoin d'un moyen flexible pour gérer les sections de la tableView, donc j'ai construit un NSMutableArray pour contenir les sections. Chaque objet dans le tableau représente une section de table via NSDictionary. Le dictionnaire a 2 clés, une chaîne pour l'en-tête de section et un tableau pour contenir les possessions de la section. J'ai une petite routine qui obtient tous les Possesions de la classe de magasin et les trie et les stocke dans les tableaux et le dictionnaire appropriés. J'ai travaillé à réécrire mon code pour l'incorporer et j'ai atteint un barrage routier confus. Comme mon application fonctionne dans le débogueur, je jette beaucoup de NSLogs pour suivre ce qui se passe. Je ne semble pas avoir de difficulté à accéder et à consigner le contenu de mon tableau de sections ou il est nsdictionaries de la plupart des méthodes de support pour la vue de la table; mais quand cellForRowAtIndexpath est appelé, les dieux du code arrêtent de sourire sur moi. D'une manière ou d'une autre, lorsque j'essaie d'accéder ou de déconnecter mon tableau de possessions, il est soudainement vide.Problème de source de données UITableView. Mon tableau de données est soudainement vide lorsque UITableView: cellForRowAtIndexPath: est appelé

Je ne peux pas pour la vie de moi comprendre cela. Cela fait maintenant un peu plus d'une journée que je me bats la tête et j'accepterais volontiers toute contribution ou aide. Ci-dessous est mes itemsViewController.h et l'implémentation. S'il vous plaît négliger le désordre des journaux et des sections commentées. J'ai essayé de comprendre cela et je les ai laissés afin que les gens puissent me dire ce que je devrais changer à propos de mon approche. Aussi, il peut être utile de noter que la table est initialement vide et l'application n'a aucun problème jusqu'à ce que j'essaie d'ajouter quelque chose.

// 
// ItemsViewController.h 
// HomePwnr 

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

@interface ItemsViewController : UITableViewController 
{ 
    NSMutableArray *sections; // variable to hold the number of sections 
} 
-(void)addNewPossession:(id)sender; 
-(void)divideSections; 
@end 

Voici la mise en œuvre

// 
// ItemsViewController.m 
// HomePwnr 

#import "ItemsViewController.h" 
#import "PossessionStore.h" 
#import "Possession.h" 

@implementation ItemsViewController 

- (id) init 
{ 
     NSLog(@"ItemsViewController init called"); 
     // Call the superclass's designated initializer 
    self = [super initWithStyle:UITableViewStyleGrouped]; 

    if (self) { 
     // create a new barItem that will send addNePossession: to itemsViewController 
     UIBarButtonItem *bbi = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:@selector(addNewPossession:)]; 

     // Set this barButtonItem as the right item in the Navigation item 
     [[self navigationItem] setRightBarButtonItem:bbi]; 

     //The Navigation item retains its buttons so release bbi 
     [bbi release]; 

     // set the title of the navigation item 
     [[self navigationItem] setTitle:@"Homepwner"]; 

     //[[self navigationItem] setLeftBarButtonItem:[self editButtonItem]]; 
    } 
    sections = [[[NSMutableArray alloc] init] retain]; // added the extra retain here to make sure my sections weren't getting released pre maturely 

     // set up sections here by dividing allPossessions 
    [self divideSections]; 
    return self; 
} 

- (id) initWithStyle:(UITableViewStyle)style 
{ 
    return [self init]; 
} 

-(void)divideSections 
{ 
    NSLog(@"divideSections called"); 
     // For simplicity we'll just empty out the sections array and rebuild it each time we add or modify a possesion 
    [sections removeAllObjects]; 

    NSArray *cheapStuff = [[NSArray alloc] initWithArray:[[PossessionStore defaultStore] possesionsFromPredicate:@"valueInDollars < 50"]]; 
    NSArray *expensiveStuff = [[NSArray alloc] initWithArray:[[PossessionStore defaultStore] possesionsFromPredicate:@"valueInDollars >= 50"]]; 


    // we'll be making an NSDictionary for each section. it will hold an array of possesions for each section and it's key will serve as the sections header 
    if ([cheapStuff count] > 0) { 
     NSMutableDictionary *section1 = [NSMutableDictionary dictionaryWithObject:cheapStuff forKey:@"Possessions"]; 
     [section1 setValue:@"Cheap Stuff" forKey:@"Header"]; 
     [sections addObject:section1]; 
     // sections now retains NSDictionary so we release it 
     [section1 release]; 
    } 
    if ([expensiveStuff count] > 0) { 
     NSMutableDictionary *section2 = [NSMutableDictionary dictionaryWithObject:expensiveStuff forKey:@"Possessions"]; 
     [section2 setValue:@"Cheap Stuff" forKey:@"Header"]; 
     [sections addObject:section2]; 
     // sections now retains NSDictionary so we release it 
     [section2 release]; 
    } 
     //now our arrays are retained by the dictionarys so we release them 
    [cheapStuff release]; 
    [expensiveStuff release]; 
    NSLog(@" End of divideSections sections holding %@", sections); 

} 

/* 
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath 
{ 
    ItemDetailViewController *detailViewController = [[[ItemDetailViewController alloc] init] autorelease]; 
     // NSArray *possessions = [[PossessionStore defaultStore] allPossessions]; 
     // give the detail view controller a pointer to the possesion object in row 
     // get the NSDictionary located at the section index, get the dictionary's array, get the possession at row index 
    Possession *p = [[[sections objectAtIndex:[indexPath section]] objectAtIndex:0] objectAtIndex:[indexPath row]]; 
    [detailViewController setPossession:p]; 


     // push it onto the navigationControllers stack 
    [[self navigationController] pushViewController:detailViewController animated:YES]; 


} 


- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath 
{ 
     ////If the table view is asking to commit the delete command 
    if (editingStyle == UITableViewCellEditingStyleDelete) { 

//PossessionStore *ps = [PossessionStore defaultStore]; 
// NSArray *possessions = [ps allPossessions]; 
// Possession *p = [possessions objectAtIndex:[indexPath row]]; 

     int section = [indexPath section]; 
     int row = [indexPath row]; 
     Possession *p = [[[sections objectAtIndex:section] objectAtIndex:0] objectAtIndex:row]; 
     [[[PossessionStore defaultStore] allPossessions] removePossession:p]; 

     // remove the row from the table view with an animation 
     [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:YES]; 
    } 
} 

-(void) tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath *)destinationIndexPath{ 
    [[PossessionStore defaultStore] movePossessionAtIndex:[sourceIndexPath row] toIndex:[destinationIndexPath row]]; 
} 
*/ 


-(void)addNewPossession:(id)sender 
{ 
    // 
    NSLog(@"addNewPossession called - sections = %@", sections); 
    [[PossessionStore defaultStore] createPossession]; 
    //NSLog(@"Possesion store now holds %@", [[PossessionStore defaultStore] allPossessions]); 

     //we've added a new possession so we'll divide out the sections again 
    [self divideSections]; 
    //NSLog(@"addNewPossession exiting - sections = %@", sections); 

     //tableview returns the tablesview 
    [[self tableView] reloadData]; 
    //NSLog(@"table view reloading data - sections = %@", sections); 

} 

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 
{ 
    int numSections = [[(NSArray *)[sections objectAtIndex:section] objectForKey:@"Possessions"] count]; 
    //NSLog(@"numberOfRowsInSection: called for section %i, returning %i.", section, numSections); 
    return numSections; 
} 

- (NSInteger) numberOfSectionsInTableView:(UITableView *)tableView 
{ 
    NSLog(@"returning number of sections: %i", [sections count]); 
     // return the count of the sections array 
    return [sections count]; 
} 

- (NSString *) tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section 
{ 
    NSLog(@"tableView:titleForHeaderInSectionCalled - sections = %@", sections); 
     //Configure the header titles based on the number of sections 
    if ([sections count] <= 1) { 
     // return simple title for only one section in table 
     NSLog(@"Returning My Stuff"); 
     return @"My Stuff"; 
    } else { 
     NSLog(@"The header returned is %@", [[sections objectAtIndex:section] objectForKey:@"Header"]); 
     // or return the key for the dictionary entry for the current section 
     return [[[sections objectAtIndex:section] objectAtIndex:section] objectForKey:@"Header"]; 
    } 
} 

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 
{ 
    NSLog(@"tableView:cellForRowAtIndexPath called for section %d, Row %d", [indexPath section], [indexPath row]); 
    NSLog(@"Sections = %@", sections); 
    NSLog(@"The Dictionary is %@", [sections objectAtIndex:[indexPath section]]); 
    //NSLog(@"thisSection array should be %@", (NSArray *)[[sections objectAtIndex:thisSection] objectForKey:@"Possessions"]); 
    //NSArray *thisSectionArray = [[sections objectAtIndex:thisSection] objectForKey:@"Possessions"]; 
     // Check for reusable cell 
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"UITableViewCell"]; 
     // If there is no cell of this type create a new one 
    if (!cell) { 
     cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"UITableViewCell"] autorelease]; 
    } 

     // get the NSDictionary located at the section index, get the dictionary's array, get the possession at row index 
     //Possession *p = [thisSection objectAtIndex:[indexPath row]]; 
     //[[cell textLabel] setText:[p description]]; 
    [[cell textLabel] setText:@"cell text"]; 

    return cell; 
} 

-(void)viewWillAppear:(BOOL)animated 
{ 
    [self divideSections]; 
    [super viewWillAppear:YES]; 
    NSLog(@"viewWillAppear called - sections = %@", sections); 
} 

-(void)viewDidUnload 
{ 
    [super viewDidUnload]; 
    NSLog(@"viewDidUnload called - sections = %@", sections); 
} 

@end 

Enfin voici mon journal d'essayer d'exécuter l'application. Le petit indicateur vert est assis juste sur la ligne où j'essaie d'enregistrer le contenu des sections pendant cellForRow ....... après le plantage de l'application.

2012-03-10 06:22:00.177 HomePwnr[44399:f803] ItemsViewController init called 
2012-03-10 06:22:00.180 HomePwnr[44399:f803] divideSections called 
2012-03-10 06:22:00.181 HomePwnr[44399:f803] End of divideSections sections holding (
) 
2012-03-10 06:22:00.188 HomePwnr[44399:f803] divideSections called 
2012-03-10 06:22:00.189 HomePwnr[44399:f803] End of divideSections sections holding (
) 
2012-03-10 06:22:00.189 HomePwnr[44399:f803] returning number of sections: 0 
2012-03-10 06:22:00.190 HomePwnr[44399:f803] returning number of sections: 0 
2012-03-10 06:22:00.191 HomePwnr[44399:f803] viewWillAppear called - sections = (
) 
2012-03-10 06:22:04.234 HomePwnr[44399:f803] addNewPossession called - sections = (
) 
2012-03-10 06:22:04.235 HomePwnr[44399:f803] divideSections called 
2012-03-10 06:22:04.237 HomePwnr[44399:f803] End of divideSections sections holding (
     { 
     Header = "Cheap Stuff"; 
     Possessions =   (
      "Shiny Gun (7R3K0): Worth $40, recorded on 2012-03-10 11:22:04 +0000" 
     ); 
    } 
) 
2012-03-10 06:22:04.238 HomePwnr[44399:f803] returning number of sections: 1 
2012-03-10 06:22:04.239 HomePwnr[44399:f803] tableView:titleForHeaderInSectionCalled - sections = (
     { 
     Header = "Cheap Stuff"; 
     Possessions =   (
      "Shiny Gun (7R3K0): Worth $40, recorded on 2012-03-10 11:22:04 +0000" 
     ); 
    } 
) 
2012-03-10 06:22:04.240 HomePwnr[44399:f803] Returning My Stuff 
2012-03-10 06:22:04.241 HomePwnr[44399:f803] tableView:titleForHeaderInSectionCalled - sections = (
     { 
     Header = "Cheap Stuff"; 
     Possessions =   (
      "Shiny Gun (7R3K0): Worth $40, recorded on 2012-03-10 11:22:04 +0000" 
     ); 
    } 
) 
2012-03-10 06:22:04.241 HomePwnr[44399:f803] Returning My Stuff 
2012-03-10 06:22:04.243 HomePwnr[44399:f803] tableView:cellForRowAtIndexPath called for section 0, Row 0 
(lldb) 
+0

Quelqu'un a des conseils? – vichudson1

Répondre

2

Eh bien, deux suggestions. Le premier est de passer à ARC (Automatic Reference Counts) afin que le compilateur s'occupe de toutes vos retenues/relâches, etc.

Votre problème immédiat se situe dans "divideSections" où vous libérez les sections 1 et 2. Les deux sont créés avec la routine de commodité dictionaryWithObject, qui renvoie un dictionnaire auto-libéré. Ainsi, même s'il est conservé par la routine d'allocation et en l'ajoutant aux sections de tableau, il a un nombre de retenue égal à zéro et peut donc disparaître à tout moment. (Vous pouvez savoir que c'est le cas en raison de la convention que seules les routines avec "allouer, copier ou conserver" retourneront les objets conservés)

La manière la plus simple de trouver ces choses est de demander à Xcode d'analyser au lieu de construire. Cela pointera vers de nombreux problèmes de mémoire comme celui-ci, ainsi que de nombreux autres problèmes.

Ce code est en référence à mon commentaire ci-dessous. La section doit être une propriété NSArray. Ensuite, vous écrivez le getter comme ceci:

-(void) sections { 
    if (!sections) { 
     <<code from dividesections that ends with...>> 
     self.sections = [NSArray arrayWithObjects: section1,section2,nil]; 
    } 
    return sections; 
} 

De cette façon, vous savez sections est toujours correcte, et ne pas avoir à vous soucier lorsque vous appelez divideSections.

+0

Merci pour votre réponse. Je pensais avoir un problème de gestion de la mémoire. À propos de l'ARC, le livre que je suis en train de parcourir ne le couvre pas puisqu'il a été publié avant ARC. J'ai pensé qu'il était préférable de suivre le livre plutôt que d'essayer de comprendre les changements pour ARC.J'ai pensé qu'une solide compréhension de la gestion de la mémoire (que je n'ai toujours pas, ressemble à une autre lecture de CH3) ne me ferait pas de mal avant d'apprendre l'ARC. Je vais mettre en œuvre les changements après mon retour à la maison après avoir lancé l'analyseur pour voir comment il pointe ces problèmes. Merci pour ce conseil aussi. Je posterai plus tard sur comment ça se passe. – vichudson1

+0

Espérons que cela fonctionne. Je voudrais également faire des sections une propriété – mackworth

+0

Par propriété, vous voulez dire mettre en place avec @property déclaration et le synthétiser? Pourquoi serait-ce nécessaire pour juste un tableau. Est-ce qu'il n'est pas déclaré une variable d'instance dans l'en-tête suffisante? Il n'est accessible que par la classe itemsViewController elle-même. – vichudson1

Questions connexes