web-dev-qa-db-fra.com

iPhone - utilisation dequeueReusableCellWithIdentifier

Je travaille sur une application iPhone qui possède un très grand UITableView avec des données extraites du Web. J'essaie donc d'optimiser sa création et son utilisation.

J'ai découvert que dequeueReusableCellWithIdentifier est très utile, mais après avoir vu beaucoup de codes sources utiliser ceci, je me demande si l'utilisation que je fais de cette fonction est la bonne.

Voici ce que les gens font habituellement:

UITableViewCell* cell = [tableView dequeueReusableCellWithIdentifier:@"Cell"];

if (cell == nil) {
  cell = [[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:@"Cell"];

// Add elements to the cell
return cell;

Et voici comment je l'ai fait:

// The cell row
NSString identifier = [NSString stringWithFormat:@"Cell %d", indexPath.row]; 

UITableViewCell* cell = [tableView dequeueReusableCellWithIdentifier:identifier];

if (cell != nil)
  return cell;

cell = [[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:identifier];
// Add elements to the cell
return cell;

La différence réside dans le fait que les utilisateurs utilisent le même identifiant pour chaque cellule. Par conséquent, la suppression de la file d'attente évite uniquement d'en affecter un nouveau.

Pour moi, le but de la file d'attente était de donner à chaque cellule un identifiant unique. Ainsi, lorsque l'application demande une cellule qu'elle a déjà affichée, ni l'allocation ni l'ajout d'élément ne doivent être effectués.

En fin de compte Je ne sais pas ce qui convient le mieux, la méthode "commune" limite l'utilisation de la mémoire de la table au nombre exact de cellules qu'elle affiche, alors que la méthode que j'utilise semble favoriser la vitesse, car elle conserve toutes les cellules calculées, mais peut entraîner une consommation de mémoire importante. (sauf s'il y a une limite interne à la file d'attente).

Ai-je tort de l'utiliser de cette façon? Ou est-ce juste le développeur, en fonction de ses besoins?

61
Jukurrpa

dequeueReusableCellWithIdentifier a pour objectif d'utiliser moins de mémoire. Si l'écran peut contenir 4 ou 5 cellules de tableau, alors, lors de la réutilisation, il suffit de disposer de 4 ou 5 cellules de tableau allouées en mémoire, même si le tableau contient 1000 entrées. 

Dans le deuxième sens, il n'y a pas de réutilisation. De la deuxième manière, il n’ya aucun avantage à utiliser simplement un tableau de cellules de tableau. Si votre table contient 1000 entrées, vous aurez 1000 cellules allouées en mémoire. Si vous envisagez de le faire, placez-les dans un tableau, indexez-le avec le numéro de ligne et renvoyez la cellule. Ce n'est pas une bonne idée pour les petites tables avec des cellules fixes qui peuvent constituer une solution raisonnable. Pour les tables dynamiques ou les grandes tables, ce n'est pas une bonne idée.

70
progrmr

En ce qui concerne l'identifiant de cellule - Au lieu d'utiliser simplement "cellule" pour l'identifiant, et au lieu d'utiliser un identifiant unique comme l'OP, pourriez-vous utiliser un "identificateur de type"? Par exemple, si ma table avait 3 types de cellules - une avec une sous-structure très compliquée, une avec seulement Style1 et une avec Style2, je devrais identifier ces trois éléments séparément, puis les reconstruire si la file d'attente revient à nil.

Par exemple:

-(UITableViewCell*)tableView:(UITableView*)tableView cellForRowAtIndexPath:(NSIndexPath*)indexPath{
    NSString* ident = @"";
    if(indexPath.section == 0) ident= @"complicated";
    if(indexPath.section == 1) ident= @"style1";
    if(indexPath.section == 2) ident = @"style2";

    UITableViewCell* cell = [tableView dequeueReusableCellWithIdentifier:ident];

    if(cell == nil){

       if(ident == @"complicated"){
          cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:ident] autorelease]; 
         // do excessive subview building
       }
       if(ident == @"style1"){
          cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyle1 reuseIdentifier:ident] autorelease]; 
       }

       if(ident == @"style2"){
          cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyle2 reuseIdentifier:ident] autorelease]; 
       }


    }
    if(ident == @"complicated"){
       // change the text/etc (unique values) of our many subviews
    }
    if(ident == @"style1"){
      [[cell textLabel] setText:@"Whatever"];
    }
    if(ident == @"style2"){
      [[cell textLabel] setText:@"Whateverelse"];
    }

    return cell; 
}

(Ce code ne fonctionnera probablement pas car je l'ai écrit ici, mais j'espère que vous aurez l'idée.) 

Je ne pense pas que Apple aurait créé l'idée de cellule réutilisable avec des identifiants s'ils souhaitaient que tous les identifiants soient "cell", vous ne pensez pas?

19
Tim

La documentation qui m'a aidé à comprendre pourquoi la méthode idiomatique (celle que vous avez décrite en premier) fonctionne le mieux est UITableViewCell, référence de classe section sur la méthode initWithStyle:reuseIdentifier:.

Le paragraphe reuseIdentifier se lit comme suit:

Vous devez utiliser le même identifiant de réutilisation pour toutes les cellules du même formulaire.

Et le paragraphe "Discussion" se lit comme suit:

L'identificateur de réutilisation est associé aux cellules (lignes) d'une vue tabulaire ayant la même configuration générale, moins le contenu de la cellule.

Ces instructions me montrent clairement que la façon idiomatique d'utiliser dequeueReusableCellWithIdentifier dans votre implémentation de tableView:cellForRowAtIndexPath: pour votre UITableViewDataSource crée un objet de cellule pour chaque ligne visible, quel que soit le nombre total de lignes disponibles.

13
Jeff

Je pense que le premier est le meilleur moyen (et comme vous l'avez dit couramment) d'implémenter une UITableView. Avec le second moyen, de la mémoire est allouée pour chaque nouvelle cellule affichée et aucune mémoire ne sera réutilisée.

4
AlexVogel

UITableView utilise en interne une cellule avec un identifiant en tant que "Modèle". Ainsi, la prochaine fois que vous (lire sous forme de tableau) tentez de désencoder, une nouvelle cellule est créée, mais l’objet stocké est utilisé comme modèle. Par conséquent, vous devez toujours mettre à jour son interface utilisateur pour refléter le contenu de la cellule selon le contexte.

Cela signifie également que UITableView s'occupe de la gestion de la mémoire des cellules pour nous-mêmes. En théorie, il n'y aura pas autant d'objets UITableViewCell que de cellules visibles. Mais dans la pratique, il y en a peut-être encore quelques-uns qui attendent que leur mémoire soit libérée.

Cela permet d'économiser beaucoup de temps, surtout dans les scénarios où vous avez 1000 cellules.

Sur tout appareil portable où la mémoire est rare, nous devrions différer l’allocation de toute mémoire au dernier moment possible et le libérer dès que son travail est terminé. dequeAndReusing une cellule réalise cela et le fait plutôt bien.

D’autre part, si votre cellule est une cellule personnalisée, nous pourrions probablement charger une nib et en extraire celle-ci . Si tel est le cas, vous pouvez utiliser un identifiant pour masquer OR. chargez-le de la plume. Il n'y a pas de différence dans la procédure.

La seule différence pourrait être dans le temps de chargement. Le fait de permettre à la vue Table de créer une nouvelle cellule en utilisant l'identifiant cellule comme modèlepourraitêtre légèrement plus rapide que le chargement depuis nib, mais il est à peine perceptible et dépend du contexte.

1
Deepak G M

Pour distinguer les cellules des autres cellules, vous pouvez utiliser la propriété tag de la cellule ou, si vous utilisez la cellule personnalisée, il est très facile d’introduire une nouvelle propriété dans une cellule personnalisée tout en sous-classant UITableViewCell.

Même si, après tout cela, vous êtes bloqué et avez encore besoin de cellules, alors vous pouvez essayer de suivre le code

UITableViewCell *cell = [self cellForRowAtIndexPath:indexPath]

alors que cela doit être évité dans la mesure où il produit la copie de la cellule mais ne renvoie pas la cellule existante alors que le contenu aura les mêmes valeurs.

0
Nikita Sharma Sahu