iPhone – uso dequeueReusableCellWithIdentifier

Estoy trabajando en una aplicación de iPhone que tiene una UITableView bastante grande con datos tomados de la web, así que estoy tratando de optimizar su creación y uso.

Descubrí que dequeueReusableCellWithIdentifier es bastante útil, pero después de ver muchos códigos fuente usando esto, me pregunto si el uso que hago de esta función es bueno.

Esto es lo que la gente suele hacer:

 UITableViewCell* cell = [tableView dequeueReusableCellWithIdentifier:@"Cell"]; if (cell == nil) { cell = [[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:@"Cell"]; // Add elements to the cell return cell; 

Y así es como lo hice:

 // 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 diferencia es que las personas usan el mismo identificador para cada celda, por lo que eliminar uno solo evita la asignación de uno nuevo.

Para mí, el objective de las colas era dar a cada celda un identificador único, de modo que cuando la aplicación solicitara una celda que ya se mostraba, no se debe hacer ni la asignación ni la adición de elementos.

En fin , no sé cuál es el mejor, el método “común” cela el uso de la memoria de la tabla al número exacto de celdas que muestra, mientras que el método que uso parece favorecer la velocidad, ya que mantiene todas las celdas calculadas, pero puede causar grandes consumo de memoria (a menos que haya un límite interno para la cola).

¿Me equivoco al usarlo de esta manera? ¿O depende del desarrollador, según sus necesidades?

El objective de dequeueReusableCellWithIdentifier es utilizar menos memoria. Si la pantalla puede caber 4 o 5 celdas de tabla, entonces con la reutilización solo necesita tener 4 o 5 celdas de tabla asignadas en la memoria incluso si la tabla tiene 1000 entradas.

En la segunda forma, no hay reutilización. No hay ninguna ventaja en el segundo camino sobre simplemente usar una matriz de celdas de tabla. Si su tabla tiene 1000 entradas, entonces tendrá 1000 celdas asignadas en la memoria. Si va a hacer eso, los pondría en una matriz e indexará la matriz con el número de fila y devolverá la celda. Para las tablas pequeñas con celdas fijas que pueden ser una solución razonable, para tablas dinámicas o grandes no es una buena idea.

En cuanto al identificador de celda, en lugar de simplemente usar “celda” para el identificador, y en lugar de usar un identificador único como el OP, ¿podría usar un “identificador de tipo”? Por ejemplo, si mi tabla tuviera 3 tipos de celdas, una con un subdispositivo muy complicado, una con solo Style1 y otra con Style2 , debería identificar esas tres por separado y luego simplemente reconstruirlas si dequeue aparece nil .

Por ejemplo:

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

(Este código probablemente no se ejecutará porque lo escribí aquí, pero espero que entiendas la idea).

No creo que Apple haya creado toda la idea de celda reutilizable con identificadores si querían que todos los identificadores fueran "cell" , ¿no crees?

La documentación que me ayudó a entender por qué la forma idiomática (la que describió primero) funciona mejor fue la sección de referencia de clase UITableViewCell en el initWithStyle:reuseIdentifier: .

La subsección reuseIdentifier dice:

Debe usar el mismo identificador de reutilización para todas las celdas de la misma forma.

Y la subsección “Discusión” dice:

El identificador de reutilización está asociado con aquellas celdas (filas) de una vista de tabla que tienen la misma configuración general, menos el contenido de celda.

Estas declaraciones me dejan claro que la forma idiomática de utilizar dequeueReusableCellWithIdentifier dentro de su implementación de tableView:cellForRowAtIndexPath: para su UITableViewDataSource crea un objeto de celda para cada fila visible , independientemente del número total de filas disponibles.

Creo que la primera es la mejor forma (y como dijiste en común) de implementar una UITableView . Con su segunda forma, se asignará memoria para cada nueva celda que se muestre y no se reutilizará ninguna memoria.

UITableView utiliza internamente una celda con un identificador como una “Plantilla”. Así que la próxima vez que (leído como una tabla) intente deque, solo creará una nueva celda pero usará el objeto almacenado como plantilla. Por lo tanto, todavía tiene que actualizar su interfaz de usuario para reflejar el contenido de la celda según el contexto.

Esto también significa que UITableView está haciendo la gestión de la memoria de las células para nosotros, per se. En teoría, solo habrá tantos objetos UITableViewCell como las celdas visibles. Pero, en la práctica, podría haber un par más esperando ser liberado de la memoria.

Esto básicamente ahorra gran cantidad de memoria, especialmente en escenarios donde tienes 1000 celdas.

En cualquier dispositivo portátil donde la memoria es un bien escaso, debemos diferir la asignación de cualquier memoria al último momento posible y liberarla en el momento en que se realiza su trabajo. dequeAndReusing y dequeAndReusing una célula logra esto y lo hace bastante bien.

Por otro lado, si su celda es una celda personalizada, entonces es muy probable que carguemos un plumín y lo extraigamos. Si este es el caso, puede usar un identificador para deque O puede cargarlo desde el plumín. No hay diferencia en el procedimiento.

La única diferencia podría ser en el tiempo de carga. Permitir que la vista Tabla cree una nueva celda usando la celda identificadora como plantilla podría ser un poco más rápida que la carga desde la punta, pero apenas se nota y depende del contexto.

Para distinguir la celda de otras celdas, puede usar la propiedad de etiqueta de la celda o si está usando la celda personalizada, entonces es muy fácil introducir cualquier propiedad nueva en la celda personalizada al subclasificar UITableViewCell .

Aunque después de todo esto estás atrapado y aún necesitas obtener una celda, puedes intentar seguir el código

UITableViewCell *cell = [self cellForRowAtIndexPath:indexPath]

mientras que debe evitarse hasta cierto punto ya que produce la copia de la celda pero no devuelve la celda existente mientras que los contenidos tendrán los mismos valores.