ios cocoa-touch asynchronous grand-central-dispatch nsoperationqueue

ios - Fondo NSOperationqueue, descargar imágenes



cocoa-touch asynchronous (3)

NSOperationQueue un NSOperationQueue para descargar imágenes (desde Twitter para Cell):

NSOperationQueue *queue = [[NSOperationQueue alloc]init]; [queue addOperationWithBlock:^{ NSString *ImagesUrl = [[NSString alloc]initWithFormat:@"http://api.twitter.com/1/users/profile_image/%@",[[status objectForKey:@"user"]objectForKey:@"screen_name"]];; NSURL *imageurl = [NSURL URLWithString:ImagesUrl]; UIImage *img = [UIImage imageWithData:[NSData dataWithContentsOfURL:imageurl]]; [[NSOperationQueue mainQueue]addOperationWithBlock:^{ if (img.size.width == 0 || [ImagesUrl isEqualToString:@"<null>"]) { [statusCell.imageCellTL setFrame:CGRectZero]; statusCell.imageCellTL.image = [UIImage imageNamed:@"Placeholder"] ; }else [statusCell.imageCellTL setImage:img];

esto funciona bien, pero cuando parece mover el desplazamiento y ver las imágenes todavía se están cargando, y están cambiando varias veces hasta que obtenga una imagen.

Y no me gusta el perfil de tiempo de diagnóstico, así que quería de alguna manera hacer que este NSOperationQueue en el fondo

También es posible mostrar cómo hacer un "Imagecache" sin necesidad de descargar una imagen ya descargada.

** (Estado = NSDiccionario de la línea de tiempo de Twitter).

edición: :( All Cell)

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString *CellIdentifier = @"Celulatime"; UITableViewCell *Cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; if ( [Cell isKindOfClass:[TimeLineCell class]] ) { TimeLineCell *statusCell = (TimeLineCell *) Cell; status = [self.dataSource objectAtIndex:indexPath.row]; statusCell.TextCellTL.text = [status objectForKey:@"text"]; statusCell.NomeCellTL.text = [status valueForKeyPath:@"user.name"]; statusCell.UserCellTL.text = [NSString stringWithFormat:@"@%@", [status valueForKeyPath:@"user.screen_name"]]; NSDate *created_at = [status valueForKey:@"created_at"]; if ( [created_at isKindOfClass:[NSDate class] ] ) { NSTimeInterval timeInterval = [created_at timeIntervalSinceNow]; statusCell.timeCellTL.text = [self timeIntervalStringOf:timeInterval]; } else if ( [created_at isKindOfClass:[NSString class]] ) { NSDate *date = [self.twitterDateFormatter dateFromString: (NSString *) created_at]; NSTimeInterval timeInterval = [date timeIntervalSinceNow]; statusCell.timeCellTL.text = [self timeIntervalStringOf:timeInterval]; } NSString *imageUrlString = [[NSString alloc]initWithFormat:@"http://api.twitter.com/1/users/profile_image/%@",[[status objectForKey:@"user"]objectForKey:@"screen_name"]];; UIImage *imageFromCache = [self.imageCache objectForKey:imageUrlString]; if (imageFromCache) { statusCell.imageCellTL.image = imageFromCache; [statusCell.imageCellTL setFrame:CGRectMake(9, 6, 40, 40)]; } else { statusCell.imageCellTL.image = [UIImage imageNamed:@"TweHitLogo57"]; [statusCell.imageCellTL setFrame:CGRectZero]; [self.imageluckluck addOperationWithBlock:^{ NSURL *imageurl = [NSURL URLWithString:imageUrlString]; UIImage *img = [UIImage imageWithData:[NSData dataWithContentsOfURL:imageurl]]; if (img != nil) { [self.imageCache setObject:img forKey:imageUrlString]; // now update UI in main queue [[NSOperationQueue mainQueue] addOperationWithBlock:^{ TimeLineCell *updateCell = (TimeLineCell *)[tableView cellForRowAtIndexPath:indexPath]; if (updateCell) { [updateCell.imageCellTL setFrame:CGRectMake(9, 6, 40, 40)]; [updateCell.imageCellTL setImage:img]; } }]; } }]; } } return Cell; }


Un par de observaciones:

  1. Probablemente deba definir un NSOperationQueue en su clase e inicializarlo en viewDidLoad (así como también en NSCache ) y agregar operaciones a esa cola, en lugar de crear una nueva NSOperationQueue para cada imagen. Además, muchos servidores limitan el número de solicitudes simultáneas que admitirán de cada cliente, así que asegúrese de establecer maxConcurrentOperationCount consecuencia.

    @interface ViewController () @property (nonatomic, strong) NSOperationQueue *imageOperationQueue; @property (nonatomic, strong) NSCache *imageCache; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; self.imageOperationQueue = [[NSOperationQueue alloc]init]; self.imageOperationQueue.maxConcurrentOperationCount = 4; self.imageCache = [[NSCache alloc] init]; } // the rest of your implementation @end

  2. Su tableView:cellForRowAtIndexPath: debe hacer que (a) inicialice la image antes de iniciar la carga de la imagen asincrónica (para que no vea allí la imagen anterior de la celda reutilizada); y (b) asegúrese de que la celda aún esté visible antes de actualizarla:

    NSString *imageUrlString = [[NSString alloc]initWithFormat:@"http://api.twitter.com/1/users/profile_image/%@",[[status objectForKey:@"user"]objectForKey:@"screen_name"]];; UIImage *imageFromCache = [self.imageCache objectForKey:imageUrlString]; if (imageFromCache) { statusCell.imageCellTL.image = imageFromCache; [statusCell.imageCellTL setFrame: ...]; // set your frame accordingly } else { statusCell.imageCellTL.image = [UIImage imageNamed:@"Placeholder"]; [statusCell.imageCellTL setFrame:CGRectZero]; // not sure if you need this line, but you had it in your original code snippet, so I include it here [self.imageOperationQueue addOperationWithBlock:^{ NSURL *imageurl = [NSURL URLWithString:imageUrlString]; UIImage *img = [UIImage imageWithData:[NSData dataWithContentsOfURL:imageurl]]; if (img != nil) { // update cache [self.imageCache setObject:img forKey:imageUrlString]; // now update UI in main queue [[NSOperationQueue mainQueue] addOperationWithBlock:^{ // see if the cell is still visible ... it''s possible the user has scrolled the cell so it''s no longer visible, but the cell has been reused for another indexPath TimeLineCell *updateCell = (TimeLineCell *)[tableView cellForRowAtIndexPath:indexPath]; // if so, update the image if (updateCell) { [updateCell.imageCellTL setFrame:...]; // I don''t know what you want to set this to, but make sure to set it appropriately for your cell; usually I don''t mess with the frame. [updateCell.imageCellTL setImage:img]; } }]; } }]; }

  3. No se necesita un manejo especial de UIApplicationDidReceiveMemoryWarningNotification porque, aunque NSCache no responde a esta advertencia de memoria, desaloja automáticamente sus objetos a medida que la memoria NSCache .

No he probado el código anterior, pero espero que entiendas la idea. Este es el patrón típico. Tu código original tenía una [ImagesUrl isEqualToString:@"<null>"] , que no veo cómo podría ser el caso, pero si necesitas alguna lógica adicional además de mi if (img != nil) ... , luego ajusta esa línea en consecuencia.



Descargar imágenes asíncronas en tabla vista usando swift 3

class ViewController: UIViewController { var queue = OperationQueue() let imageURLs = ["https://amazingslider.com/wp-content/uploads/2012/12/dandelion.jpg", "https://media.treehugger.com/assets/images/2011/10/tiger-running-snow.jpg.600x315_q90_crop-smart.jpg", "https://www.w3schools.com/css/trolltunga.jpg", "https://www.w3schools.com/w3css/img_lights.jpg", "https://cdn.pixabay.com/photo/2015/04/28/20/55/aurora-borealis-744351_960_720.jpg", "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcS2L0pETnybA5sld783iz1mgtOFS8vxBTjB4tYXeRtQWDxig3dc"] override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view, typically from a nib. } } extension ViewController: UITableViewDelegate, UITableViewDataSource { func numberOfSections(in tableView: UITableView) -> Int { return 1 } func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return imageURLs.count } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell: ImagesTableViewCell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! ImagesTableViewCell var img: UIImage! let operation = BlockOperation(block: { img = Downloader.downloadImageWithURl(self.imageURLs[indexPath.row]) }) operation.completionBlock = { DispatchQueue.main.async { cell.imgView?.image = img } } queue.addOperation(operation) return cell } } class Downloader { class func downloadImageWithURl(_ url: String) -> UIImage! { if let data = try? Data(contentsOf: URL(string: url)!) { return UIImage(data: data)! } return nil } }