7

我创建了一个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];

这工作正常,但是当它似乎移动滚动并查看图像时仍在加载,并且它们会更改几次,直到您获得图片。

而且我不喜欢时间的诊断配置文件,所以我想以某种方式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;
    }
4

3 回答 3

12

几点观察:

  1. 您可能应该NSOperationQueue在您的类中定义 a 并将其初始化viewDidLoad(以及 a 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:应该(a)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>"],我看不出这种情况,但如果您需要一些额外的逻辑,除了 my if (img != nil) ...,然后相应地调整该行。

于 2013-01-21T18:02:25.463 回答
1

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

它还具有取消功能以在用户按下取消按钮的情况下取消操作。

于 2014-09-18T06:08:39.810 回答
0

使用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
    }

}
于 2017-09-26T11:09:14.973 回答