0

我必须从通讯录中获取联系人,如果在UITableView.

我使用库获取所有联系人ABContactsHelper,然后异步获取UITableView使用 GCD 块中可见行的照片。

我提到了一个 Apple 示例代码,它等待 UITableView 完成滚动,获取 VisibleNSIndexPath并创建线程来获取照片。到目前为止,我的问题是两个方面。

首先,如果用户滚动、停止、滚动和停止并多次滚动,则会生成太多线程来获取照片,这会降低应用程序的速度。

其次,当线程返回以在缓存中设置照片以及UITableViewCell然而,对的引用UIImageView现在被重用于 中的另一条记录UITableViewCell,因此照片被放置在错误的记录上,最终被正确的记录替换,当线程为那个特定的记录回报。

这是我在停止滚动cellForRowAtIndexPath时以及UITableView停止滚动时使用的代码。

- (void)loadImagesLazilyForIndexPath:(NSIndexPath *)indexPath photo:(UIImageView *)photo contact:(ContactModel *)contact
{

    if (!self.tableView.isDragging && !self.tableView.isDecelerating) {
        UIImage *thePhoto = [self.imagesForContacts objectForKey:indexPath];
        if (!thePhoto) {
            // NSLog(@"Photo Not Found - Now Fetching %@", indexPath);
            dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
                @autoreleasepool {
                    UIImage *image = [[JMContactsManager sharedContactsManager] photoForContact:contact];
                    if (!image)
                        image = self.noProfilePhoto;
                    [self.imagesForContacts setObject:image forKey:indexPath];
                    dispatch_async(dispatch_get_main_queue(), ^{
                        // NSLog(@"Photo Fetched %@", indexPath);
                        @autoreleasepool {
                            NSArray *visiblePaths = [self.tableView indexPathsForVisibleRows];
                            BOOL visible = [visiblePaths indexOfObjectPassingTest:^BOOL(NSIndexPath * ip, NSUInteger idx, BOOL *stop) {
                                if (ip.row == indexPath.row && ip.section == indexPath.section) {
                                    *stop = YES;
                                    return 1;
                                }
                                return 0;
                            }];
                            if (visible)
                                photo.image = [self.imagesForContacts objectForKey:indexPath];
                            [[NSURLCache sharedURLCache] removeAllCachedResponses];
                        }
                    });
                }
            });
        } else {
            // NSLog(@"Photo Was Found %@", indexPath);
            @autoreleasepool {
                photo.image = [self.imagesForContacts objectForKey:indexPath];
            }
        }
    }
}
4

2 回答 2

1

正如@Andrea 所提到的,您应该使用 NSOperationQueue,它使您能够取消排队的任务。通过 indexPath 将图像缓存索引到表中并不可靠,因为给定元素的索引路径可能会发生变化(尽管在您的特定情况下可能不会)。您可能会考虑使用 ABRecord.uniqueId 来索引您的图像缓存。

在任何情况下,它都不会解决您的图像为同一个单元格设置两次或更多次的问题。发生这种情况是因为 UITableView 没有为每个项目分配一个视图,而是管理一个 UITableCellViews 池,它每次都重复使用它。你可以做的是沿着以下几行:

// Assuming your "ContactCellView" inherits from UITableCellView and has a contact property
// declared as follows: @property (retain) ABRecord *contact.

- (void) setContact:(ABRecord*)contact
{
    _contact = contact;
    __block UIImage *thePhoto = [self.imagesForContacts objectForKey:contact.uniqueId];
    if (thePhoto == nil) {
        _loadImageOp = [NSBlockOperation blockOperationWithBlock:^(void) {

            // Keep a local reference to the contact because it might change on us at any time.
            ABRecord *fetchContact = contact;

            // Fetch the photo as you normally would
            thePhoto = [[JMContactsManager sharedContactsManager] photoForContact:fetchContact];
            if (thePhoto == nil)
                thePhoto = self.noProfilePhoto;

            // Only assign the photo if the contact has not changed in the mean time.
            if (fetchContact == _contact)
                _contactPhotoView.image = thePhoto;
        }];        
    } else {
       _contactPhotoView.image = thePhoto;
    }
}
于 2013-05-02T08:31:46.930 回答
1

对于这种功能,我会使用 NSOperation 和 NSOperationQueue,它们构建在 GCD 之上,但它让您有机会取消操作。您可以检查哪些操作不再可见并取消它们。通过这种方式,您可以控制参考“离开”。
我还看到另一个可能导致“问题”的问题,您似乎正在将图像缓存在 NSMutableDictionary 中,不是吗?还是您使用的是 NSCache?如果它是一个 NScache 很好,但大多数可变对象“自然”不是线程安全
的 提高队列的优先级:-)

于 2013-05-02T06:06:41.977 回答