Problemas de GCD y KVO

Mi aplicación quiere obtener la lista de álbumes del iPhone y todas las fotos en cierto álbum.

En la aplicación enumero las fotos en un álbum del iphone. Como puede haber muchas fotos de cierto álbum, considerando el rendimiento, uso GCD: dispatch_async. Pero siempre falla cuando se actualiza la celda de vista de tabla que es invocada por KVO. No tengo ni idea de si uso KVO o GCD de forma incorrecta.

Ahora, alternativamente, uso performSelectorInBackground: reemplazo de dispatch_async. Ahora la aplicación no se bloquea, pero el rendimiento de la aplicación es deficiente: el título de la celda solo se mostrará cuando la toque o se desplace por la vista de la tabla cuando haya muchas fotos. En otras palabras, el hilo principal debe estar bloqueado.

Se adjunta el código y el código principal está en AlbumListViewController.m.

¿Alguien puede ayudarme a comprobarlo?

Solo quiero saber: 1 por qué la aplicación se bloquea si uso dispatch_async 2 cómo puedo mejorar el rendimiento en caso de muchas fotos.

Gracias.

A continuación se muestra mi código:

// // RootViewController.h // AlbumDemo #import @interface RootViewController: UITableViewController {NSMutableArray * _listArray; } @property (no atómico, conservar) NSMutableArray * listArray; @end // RootViewController.m #import "RootViewController.h" #import #import "AlbumListViewController.h" NSString * thumnail = @ "thumnail"; NSString * albumName = @ "albumName"; NSString * albumNum = @ "albumNum"; NSString * albumGroup = @ "albumGroup"; @implementación RootViewController @synthesize listArray = _listArray; #pragma - Función #pragma - (void) setUp {_listArray = [[NSMutableArray alloc] initWithCapacity: 1]; self.title = @ "Álbumes"; } - (void) fetchAlbumList {ALAssetsLibrary * assetLib = [[[ALAssetsLibrary alloc] init] autorelease]; ALAssetsFilter * fileter = [ALAssetsFilter allPhotos]; [assetLib enumerateGroupsWithTypes: ALAssetsGroupAll usingBlock: ^ (ALAssetsGroup * grupo, BOOL * stop) {if (grupo) {[grupo setAssetsFilter: fileter]; NSString * _groupName = [group valueForProperty: ALAssetsGroupPropertyName]; NSNumber * _groupNum = [NSNumber numberWithInteger: [group numberOfAssets]]; UIImage * _groupImage = [UIImage imageWithCGImage: [group posterImage]]; NSDictionary * dic = [NSDictionary dictionaryWithObjectsAndKeys: _groupName, albumName, _groupNum, albumNum, _groupImage, thumnail, group, albumGroup, nil]; [_listArray addObject: dic]; [self.tableView reloadData]; } else {NSLog (@ "_ listArray:% @", _ listArray); }} failureBlock: ^ (NSError * error) {NSLog (@ "Error:% @", error) ;; }]; } #pragma - #pragma Ciclo de elevación de ViewController - (void) viewDidLoad {[super viewDidLoad]; [autoconfiguración]; [self fetchAlbumList]; } - (vacío) viewWillAppear: (BOOL) animado {[super viewWillAppear: animado]; } - (void) viewDidAppear: (BOOL) animado {[super viewDidAppear: animado]; } - (vacío) viewWillDisappear: (BOOL) animado {[super viewWillDisappear: animado]; } - (void) viewDidDisappear: (BOOL) animado {[super viewDidDisappear: animado]; } - (CGFloat) tableView: (UITableView *) tableView heightForRowAtIndexPath: (NSIndexPath *) indexPath {return 50; } // Personaliza el número de secciones en la vista de tabla.
 - (NSInteger) numberOfSectionsInTableView: (UITableView *) tableView {return 1; } - (NSInteger) tableView: (UITableView *) tableView numberOfRowsInSection: (NSInteger) sección {return [_listArray count]; } // Personaliza la apariencia de las celdas de la vista de tabla.
 - (UITableViewCell *) tableView: (UITableView *) tableView cellForRowAtIndexPath: (NSIndexPath *) indexPath {static NSString * CellIdentifier = @ "Cell"; UILabel * nameLab = nil; UILabel * numLab = nil; UIImageView * thumnailImage = nil; UIFont * font = [UIFont boldSystemFontOfSize: 18]; UITableViewCell * cell = [tableView dequeueReusableCellWithIdentifier: CellIdentifier]; if (celda == nil) {celda = [[[UITableViewCell alloc] initWithStyle: UITableViewCellStyleDefault reuseIdentifier: CellIdentifier] autorelease]; cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; thumnailImage = [[UIImageView alloc] initWithFrame: CGRectMake (0, 0,50, 50)]; thumnailImage.tag = 100; [cell.contentView addSubview: thumnailImage]; [lanzamiento de thumnailImage]; nameLab = [[UILabel alloc] initWithFrame: CGRectMake (60, 10, 100, 30)]; nameLab.tag = 200; nameLab.backgroundColor = [UIColor clearColor]; nameLab.font = fuente; [cell.contentView addSubview: nameLab]; [lanzamiento de nameLab]; numLab = [[UILabel alloc] initWithFrame: CGRectMake (200, 10, 50, 30)]; numLab.tag = 300; numLab.backgroundColor = [UIColor clearColor]; numLab.textColor = [UIColor grayColor]; numLab.font = fuente; [cell.contentView addSubview: numLab]; [lanzamiento de numLab]; } else {thumnailImage = (UIImageView *) [cell.contentView viewWithTag: 100]; nameLab = (UILabel *) [cell.contentView viewWithTag: 200]; numLab = (UILabel *) [cell.contentView viewWithTag: 300]; } NSDictionary * dic = [self.listArray objectAtIndex: indexPath.row]; thumnailImage.image = (UIImage *) [dic valueForKey: thumnail]; NSString * title = [dic valueForKey: albumName]; CGSize titleSize = [tamaño del título con fuente: fuente]; CGRect rect = nameLab.frame; rect.size = titleSize; nameLab.frame = rect; nameLab.text = título; rect = numLab.frame; rect.origin.x = 60 + nameLab.frame.size.width + 10; numLab.frame = rect; numLab.text = [NSString stringWithFormat: @ "(% d)", [[dic valueForKey: albumNum] intValue]]; // Configurar la celda.
 celda de retorno; } - (vacío) tableView: (UITableView *) tableView didSelectRowAtIndexPath: (NSIndexPath *) indexPath {NSDictionary * dic = [self.listArray objectAtIndex: indexPath.row]; AlbumListViewController * viewController = [[AlbumListViewController alloc] initWithAssetGroup: [dic valueForKey: albumGroup]]; [self.navigationController pushViewController: viewController animado: SÍ]; [versión de viewController]; [tableView deselectRowAtIndexPath: indexPath animado: SÍ]; } - (void) didReceiveMemoryWarning {// Libera la vista si no tiene una supervista.
 [super didReceiveMemoryWarning]; // Renunciar a la propiedad de los datos, imágenes, etc. almacenados en caché que no estén en uso.
 } - (vacío) viewDidUnload {[super viewDidUnload]; // Renunciar a la propiedad de todo lo que se pueda recrear en viewDidLoad o bajo demanda.
 // Por ejemplo: self.myOutlet = nil; } - (vacío) dealloc {My_Release (_listArray); [super desalloc]; } @end // AlbumListViewController.h // AlbumDemo #import #import @interface AlbumListViewController: UITableViewController {NSMutableArray * _marr; ALAssetsGroup * _assetsGroup; } @property (no atómico, conservar) NSMutableArray * list; @property (no atómico, retener) ALAssetsGroup * assetsGroup; - (id) initWithAssetGroup: (ALAssetsGroup *) grupo; @end // AlbumListViewController.m // AlbumDemo #import "AlbumListViewController.h" @interface PhotoObj: NSObject {NSString * _name; UIImage * _thumbnail; UIImage * _fullImage; } @property (nonatomic, copy) NSString * nombre; @property (no atómico, retener) UIImage * miniatura; @property (no atómico, retener) UIImage * fullImage; @end @implementation PhotoObj @synthesize name = _name; @synthesize thumbnail = _thumbnail, fullImage = _fullImage; - (vacío) desbloqueo {My_Release (_thumbnail); My_Release (_fullImage); My_Release (_name); [super desalloc]; } @end @interface AlbumListViewController () - (NSMutableArray *) lista; - (NSUInteger) countOfList; - (id) objectInListAtIndex: (NSUInteger) idx; - (void) insertObject: (id) anObject inListAtIndex: (NSUInteger) idx; - (id) objectInListAtIndex: (NSUInteger) idx; - (vacío) removeObjectFromListAtIndex: (NSUInteger) idx; - (void) replaceObjectInListAtIndex: (NSUInteger) idx withObject: (id) anObject; - (vacío) setList: (NSMutableArray *) _ arr; @end @implementation AlbumListViewController @synthesize assetsGroup = _assetsGroup; - (id) initWithAssetGroup: (ALAssetsGroup *) grupo {self = [self initWithStyle: UITableViewStylePlain]; if (self) {_marr = [[NSMutableArray alloc] initWithCapacity: 1]; self.assetsGroup = grupo; self.tableView.delegate = self; self.tableView.dataSource = self; } return self; } - (id) initWithStyle: (UITableViewStyle) style {self = [super initWithStyle: style]; if (self) {// Inicialización personalizada} return self; } - (vacío) dealloc {My_Release (_marr); My_Release (_assetsGroup); [self removeObserver: self forKeyPath: @ "lista"]; [super desalloc]; } - (void) didReceiveMemoryWarning {// Libera la vista si no tiene una supervista.
 [super didReceiveMemoryWarning]; } #pragma mark - Ver ciclo de vida - (void) parseAssetGroup {[_marr removeAllObjects]; [self.assetsGroup enumerateAssetsUsingBlock: ^ (ALAsset * resultado, índice NSUInteger, BOOL * stop) {if (resultado) {PhotoObj * obj = [[PhotoObj alloc] init]; obj.thumbnail = [UIImage imageWithCGImage: [miniatura del resultado]]; ALAssetRepresentation * represention = [result defaultRepresentation]; obj.fullImage = [UIImage imageWithCGImage: [representacion fullScreenImage]]; obj.name = [[url de representación] cadenaAbsoluta]; [self willChangeValueForKey: @ "lista"]; [self insertObject: obj inListAtIndex: [_ recuento de marr]]; [self didChangeValueForKey: @ "lista"]; My_Release (obj); }}]; } - (void) viewDidLoad {[super viewDidLoad]; [self addObserver: self forKeyPath: @ "lista" opciones: NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld contexto: NULL]; / * si performSelectorInBackground, el rendimiento es pobre ya que el título de la celda se mostrará en mucho tiempo y ahora parece que el hilo principal está bloqueado * / [self performSelectorInBackground: @selector (parseAssetGroup) withObject: nil]; / * al usar dispatch_async siempre falla ya que dice que algo está mal con la actualización de la vista de tabla * / // dispatch_async (dispatch_get_main_queue (), ^ {// [self parseAssetGroup]; //}); } - (vacío) viewDidUnload {[super viewDidUnload]; } #pragma mark - Fuente de datos de vista de tabla - (CGFloat) tableView: (UITableView *) tableView heightForRowAtIndexPath: (NSIndexPath *) indexPath {return 50; } - (NSInteger) numberOfSectionsInTableView: (UITableView *) tableView {// Devuelve el número de secciones.
 return 1; } - (NSInteger) tableView: (UITableView *) tableView numberOfRowsInSection: (NSInteger) section {// Devuelve el número de filas en la sección.
 return [_marr count]; } - (UITableViewCell *) tableView: (UITableView *) tableView cellForRowAtIndexPath: (NSIndexPath *) indexPath {static NSString * CellIdentifier = @ "Cell"; UIImageView * thumbNail = nil; UILabel * nameLab = nil; UITableViewCell * cell = [tableView dequeueReusableCellWithIdentifier: CellIdentifier]; if (celda == nulo) {celda = [[[UITableViewCell alloc] initWithStyle: UITableViewCellStyleDefault reuseIdentifier: CellIdentifier] autorelease]; cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; thumbNail = [[UIImageView alloc] initWithFrame: CGRectMake (0, 0, 50, 50)]; thumbNail.tag = 99; [cell.contentView addSubview: thumbNail]; [liberación de thumbNail]; nameLab = [[UILabel alloc] initWithFrame: CGRectMake (60, 10, 240, 40)]; nameLab.numberOfLines = 2; nameLab.font = [UIFont systemFontOfSize: 16]; nameLab.tag = 199; [cell.contentView addSubview: nameLab]; [lanzamiento de nameLab]; } else {thumbNail = (UIImageView *) [cell.contentView viewWithTag: 99]; nameLab = (UILabel *) [cell.contentView viewWithTag: 199]; } // Configurar la celda ...
 

preguntado el 04 de julio de 11 a las 05:07

1 Respuestas

Hoy me encontré exactamente con el mismo problema. En resumen, la razón es que no puede realizar tareas relacionadas con UIKit, como actualizar una tabla, o en mi caso una vista de texto, desde la cola de despacho en segundo plano. Consulte el enlace a continuación para obtener más detalles.

comparación GCD frente a performSelectorInBackground: dispatch_async no en segundo plano

Una posible solución es la siguiente: en lugar de asignar sus datos nuevos en su bloque de actualización directamente a la variable KVO que causa el bloqueo, envía otro bloque que hace esto al cola principal, desde el interior de su bloque de actualización. Si usa la función dispatch_async_f para hacer esto, puede pasar un puntero a sus datos como contexto.

Me gusta:

dispatch_async(yourQueue, ^() {
  NSArray *data;
  // do stuff to alloc and fill the array
  // ...
  dispatch_async(dispatch_get_main_queue(), ^() {
    myObj.data = data; // the assignment, which triggers the KVO.
  });
});

Para mí, esto funciona sin retener y publicar los datos. No estoy seguro, si esto es correcto.

contestado el 23 de mayo de 17 a las 12:05

puede que no importe con el hilo principal / de fondo como lo hice en la otra aplicación de la misma manera y funciona bien. Así que todavía no sé cuál es el problema. - escorpio

tienes razón. La tarea relacionada con la interfaz de usuario debería implementarse en dispatch_get_main_queue, mientras que otras deberían ser mejores que no. Acabo de encontrar la diferencia entre los tipos de colas. gracias por tu sugerencia. - escorpio

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.