图像持久性和延迟加载与Dispatch_Async冲突

时间:2013-01-17 21:13:55

标签: ios objective-c uiimageview grand-central-dispatch

我正在使用提要阅读器,我正在使用nsxmlparser解析rss提要。我还有从CDATA块中获取的缩略图对象。

-(void)parser:(NSXMLParser *)parser foundCDATA:(NSData *)CDATABlock
{
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

    NSString *someString = [[NSString alloc] initWithData:CDATABlock encoding:NSUTF8StringEncoding];
    NSString *storyImageURL = [self getFirstImageUrl:someString];

    NSURL *tempURL = [NSURL URLWithString:storyImageURL];

    NSData *tempData = [NSData dataWithContentsOfURL:tempURL];
    thumbnail = [UIImage imageWithData:tempData];

    });
}

我使用归档和编码和解码方法来保存图像和其他tableview对象。

问题是,当我按原样使用上面的代码时,它不会保留图像,而其他tableview对象(如标题和发布日期)仍然存在。但是当我使用上面的代码而没有dispatch_async时,它会开始持久化图像,但会锁定用户界面。

如何在不锁定用户界面的情况下保留图像?

请不要回答一些库来存储和缓存图像或Apple的延迟加载示例。感谢

更新

根据提供的答案以及斯坦福在iTunes中关于多线程的讲座后,我已实施上述代码如下:

  NSString *someString = [[NSString alloc] initWithData:CDATABlock encoding:NSUTF8StringEncoding];
    NSString *storyImageURL = [self getFirstImageUrl:someString];
    NSURL *tempURL = [NSURL URLWithString:storyImageURL];

    dispatch_queue_t downloadQueue = dispatch_queue_create("image downloader", NULL);

    dispatch_async(downloadQueue, ^{
        NSData *tempData = [NSData dataWithContentsOfURL:tempURL];

        dispatch_async(dispatch_get_main_queue(), ^{
            thumbnail = [UIImage imageWithData:tempData];
        });
    });
}

从逻辑上讲,现在一切都是正确的但仍然没有用。我都被阻止如何解决这个问题。

更新

我正在使用模型视图控制器存储方法,我的存储和连接代码在单独的文件中定义。下面是我使用NSKeyedArchiver的代码。

- (FeedChannel *) FeedFetcherWithCompletion:(void (^)(FeedChannel *, NSError *))block {

    NSURL *url = [NSURL URLWithString:@"http://techcrunch.com/feed/"];
    NSURLRequest *req = [NSURLRequest requestWithURL:url];

    FeedChannel *channel = [[FeedChannel alloc] init];

    TheConnection *connection = [[TheConnection alloc] initWithRequest:req];

    NSString *pathOfCache = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
    pathOfCache = [pathOfCache stringByAppendingPathComponent:@"check.archive"];

    FeedChannel *channelCache = [NSKeyedUnarchiver unarchiveObjectWithFile:pathOfCache];


    if (!channelCachel) {
        channelCache = [[FeedChannel alloc] init];
    }
    FeedChannel *channelCopy = [channelCache copy];

    [connection setCompletionBlock:^(FeedChannel *obj, NSError *err) {
        if (!err) {
            [channelCopy addItemsFromChannel:obj];
            [NSKeyedArchiver archiveRootObject:channelCopy toFile:pathOfCache];
        }
        block(channelCopy, err);
    }];

    [connection setXmlObject:channel];
    [connection start];

    return channelCache;
}

及以下是我的编码和解码方法。

- (void)encodeWithCoder:(NSCoder *)aCoder
{
    [aCoder encodeObject:title forKey:@"title"];
    [aCoder encodeObject:link forKey:@"link"];
    [aCoder encodeObject:creator forKey:@"creator"];
    [aCoder encodeObject:pubDate forKey:@"pubDate"];
    [aCoder encodeObject:thumbnail forKey:@"thumbnail"];
    //[aCoder encodeObject:UIImagePNGRepresentation(thumbnail) forKey:@"thumbnail"];
    [aCoder encodeObject:publicationDate forKey:@"publicationDate"];

}

- (id)initWithCoder:(NSCoder *)aDecoder
{
    self = [super init];
    if (self)
    {
        [self setTitle:[aDecoder decodeObjectForKey:@"title"]];
        [self setLink:[aDecoder decodeObjectForKey:@"link"]];
        [self setCreator:[aDecoder decodeObjectForKey:@"creator"]];
        [self setPubDate:[aDecoder decodeObjectForKey:@"pubDate"]];
        [self setThumbnail:[aDecoder decodeObjectForKey:@"thumbnail"]];
        //[self setThumbnail:[UIImage imageWithData:[aDecoder decodeObjectForKey:@"thumbnail"]]];
        [self setPublicationDate:[aDecoder decodeObjectForKey:@"publicationDate"]];

    }
    return self;
}

基于答案,我不知道是否必须使用一些额外的代码来保存图像。因为标题,创作者等其他对象都能准确保存。

1 个答案:

答案 0 :(得分:5)

在阅读您的评论后,我倾向于认为该问题与您如何相对于dispatch_async块保留图像有关。我会尝试解释,但这只是猜测,因为我不知道你在哪里坚持图像:

  1. 在某个时刻执行了parser:foundCDATA:并且您异步检索了您的图像;

  2. 图像将不会存在一段时间(直到同步块完成),因此thumbnail ivar / property在完成parser:foundCDATA:后的一段时间内将没有正确的值;

  3. 现在,如果您在异步块完成之前保留thumbnail,则会保留错误的(nil?)值;

  4. 如果你不是异步地执行图像的提取,那么一切正常,这让我觉得你过早地保存图像 (在上面的意义上)。

  5. 对此的修复只会在异步块完成后保留图像。这可能意味着:

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    
      NSString *someString = [[NSString alloc] initWithData:CDATABlock encoding:NSUTF8StringEncoding];
      NSString *storyImageURL = [self getFirstImageUrl:someString];
    
      NSURL *tempURL = [NSURL URLWithString:storyImageURL];
    
      NSData *tempData = [NSData dataWithContentsOfURL:tempURL];
      thumbnail = [UIImage imageWithData:tempData];
    
      <code here to make the thumbnail be persisted>
    
    });
    

    <code here to make the thumbnail be persisted>可以是方法调用,通知帖子,适合您当前的设计。如果持久性代码不是线程安全的,你可能还需要使用调用主线程的技巧:

      dispatch_async(dispatch_get_main_queue(), ^{
          <persist thumbnail>;
      }
    

    希望它有所帮助。

    编辑:

    我们聊天后,上述分析得到确认。实际上,您不只是下载一个图像:您正在下载一堆图像,这些图像都属于同一个“通道”。这意味着只有在下载了所有这些图像后才能执行应用于整个通道的持久操作。

    我已经分享了你的要点并添加了一些逻辑,允许使用GCD dispatch_group来跟踪所有下载操作。你可以找到它here