NSOperationqueue背景,下载图片(NSOperationqueue backgroun

2019-07-18 12:12发布

我创建了一个NSOperationQueue下载图片(来自Twitter的细胞):

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];

这个工作很好,但是当它出现移动滚动和查看图像仍在加载,并且他们正在改变几次,直到你得到的图片。

而且我不喜欢时间的诊断资料,所以我想以某种方式使这个NSOperationQueue的背景

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

**(状态= 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;
    }

Answer 1:

一对夫妇的意见:

  1. 你应该定义一个NSOperationQueue在你的类和初始化viewDidLoad (以及一个NSCache )和添加操作到队列中,而不是创建一个新的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:应(一)初始化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没有这种内存警告作出反应,它会自动内存变低驱逐它的对象。

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



Answer 2:

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

它还具有取消功能,以消除在壳体用户按压操作取消按钮。



Answer 3:

使用SWIFT 3 tableview中异步下载图像

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
    }

}


文章来源: NSOperationqueue background, download images