1

我创建了一个继承自 NSOperation 的 fetchImageOperation 类。它使用 URL 获取图像,然后在完成后触发块。我的问题是,如果我有以下代码:

    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"Cell";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];

    Customer *customer = [_customers objectAtIndex:[indexPath row]];

    FetchImageOperation *imageOperation = [[FetchImageOperation alloc] initWithURL:[NSURL URLWithString:vegetable.imageURL]];

    cell.nameLabel.text = customer.name;

    imageOperation.fetchImageOperationCompletionBlock = ^(UIImage *image)
    {
        [cell.thumbnailImageView setImage:image];
    };

    [_queue addOperation:imageOperation];

    return cell;
}

setImage 方法是默认在主线程(UI Thread)上调用的吗?我可以确认上面的代码有效,它确实设置了 thumbnailaImageView 元素的图像属性。

FetchImageOperation.m 文件:

@implementation FetchImageOperation

-(id) initWithURL:(NSURL *)url
{
    self = [super init];
    self.url = url;
    return self;
}


-(void) main
{
    NSData *data = [NSData dataWithContentsOfURL:_url];
    UIImage *image = [UIImage imageWithData:data];

    if(self.fetchImageOperationCompletionBlock != nil)
    {
        self.fetchImageOperationCompletionBlock(image);
    }
}

@end
4

3 回答 3

1

绝不会在主线程上自动调用任何 UI 方法。在这种情况下,它将在您的NSOperation子类main方法正在运行的任何线程上调用。

你应该使用类似的东西

dispatch_async(dispatch_get_main_queue(), ^{
    if(self.fetchImageOperationCompletionBlock != nil) {
        self.fetchImageOperationCompletionBlock(image);
    }
});

如果你想保证所有的fetchImageOperationCompletionBlocks 都在主线程上触发。并且由于单元格重用,您不希望调用单元格本身的属性,而是将索引路径保存到块中的单元格,并在该索引路径处使用单元格,或其他内容。

于 2013-06-27T22:06:53.667 回答
0

该块在与操作相同的线程上调用。所以不,它是在后台线程上调用的,所以它不能正常工作。

此外,当块完成时,您不知道该单元是否仍在用于相同的事情或是否已被重复使用。

与其做你正在做的事情,不如考虑使用类似SDWebImage.

于 2013-06-27T22:09:26.127 回答
0

您可以将单元格的 indexPaths 和 FetchImageOperation 保存在字典中,以便能够在应用程序完成后控制操作进度和单元格访问:

NSMutableDictionary *operations; // Define and initialise dictionary to hold operations   

- (void)(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    // ...

    UIImage *image = [customer image];
    if (image == nil) {
        image = [UIImage imageNamed:@"ImageHolder"];
        if (![[operations allKeys] containsObject:indexPath]) {
            FetchImageOperation *imageOperation = [[FetchImageOperation alloc] initWithURL:[NSURL URLWithString:vegetable.imageURL]];
            imageOperation.fetchImageOperationCompletionBlock = ^(UIImage *image) {
                // Do updates on the main thread
                dispatch_async(dispatch_get_main_queue(), ^{
                    UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath]
                    [cell.thumbnailImageView setImage:image];
                    [operations removeObjectForKey:indexPath];
                };
            };
            [operations setObject:imageOperation forKey:indexPath];
            [_queue addOperation:imageOperation];
        }
    }
}

// You should cancel image operations if cell goes out from the screen
- (void)tableView:(UITableView *)tableView didEndDisplayingCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
    NSOperation *operation = [operations objectForKey:indexPath];
    [operation cancel];
}
于 2013-06-27T22:59:04.353 回答