NSOperationqueue背景,下载图像

Lcs*_*est 7 cocoa-touch asynchronous nsoperationqueue grand-central-dispatch ios

我创建了一个NSOperationQueue下载图像(从Twitter获取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];
Run Code Online (Sandbox Code Playgroud)

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

而且我不喜欢诊断时间的轮廓,所以我想以某种方式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;
    }
Run Code Online (Sandbox Code Playgroud)

Rob*_*Rob 12

几点意见:

  1. 您可能应该NSOperationQueue在类中定义一个并在其中初始化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
    
    Run Code Online (Sandbox Code Playgroud)
  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];
                    }
                }];
            }
        }];
    }
    
    Run Code Online (Sandbox Code Playgroud)
  3. 不需要特殊处理,UIApplicationDidReceiveMemoryWarningNotification因为虽然NSCache没有响应此内存警告,但它会在内存变低时自动驱逐其对象.

我没有测试过上面的代码,但希望你能得到这个想法.这是典型的模式.你的原始代码有一个检查[ImagesUrl isEqualToString:@"<null>"],我不知道怎么可能是这种情况,但如果除了我之外还需要一些额外的逻辑if (img != nil) ...,那么相应地调整该行.

  • @Umka 1.是的,你可以使用`dispatch_async()`但是你无法控制并发请求的数量.这就是我更喜欢"NSOperationQueue"的原因.2.您可以使用`cell.imageView.image = nil`,但默认的`UITableViewCell`有时会根据是否存在`image`来重新格式化单元格,因此我总是使用空白图像来避免重新格式化单元格(或者更糟糕的是,加载图像时无法重新格式化单元格).3.您有时会使用自定义单元格布局来避免我在前一点中提到的问题(或者如果它不符合标准布局). (2认同)
  • 是我的错,现在正在完美运行:D谢谢朋友,我本周正在努力工作. (2认同)