使用异步 NSURLConnection 中的数据填充 NSImage

2024-01-12

我在尝试弄清楚如何使用从我的桌面应用程序(不是 iPhone 应用程序!!)中的异步 NSURLConnection 返回的数据填充 NSImage 时遇到了困难。

情况是这样的。

我有一个使用自定义单元格的表格。每个自定义单元格中都有一个从 Web 服务器拉取的 NSImage。为了填充图像,我可以轻松地执行同步请求:

myThumbnail = [[NSImage alloc] initWithContentsOfFile:myFilePath];

这样做的问题是,该表会阻塞,直到填充图像为止(显然因为它是同步请求)。在大桌子上,这使得滚动变得难以忍受,但如果图像尺寸很大,即使只是在第一次运行时填充图像也可能很乏味。

因此,我创建一个异步请求类,它将按照以下方式在自己的线程中检索数据苹果的文档 http://developer.apple.com/Mac/library/documentation/Cocoa/Conceptual/URLLoadingSystem/Tasks/UsingNSURLConnection.html。那里没问题。我可以看到正在提取和填充的数据(通过我的日志文件)。

我遇到的问题是,一旦获得数据,我需要回调到我的调用类(自定义表视图)。

我的印象是我可以做这样的事情,但它不起作用,因为(我假设)我的调用类真正需要的是一个委托:

NSImage * myIMage;
myImage = [myConnectionClass getMyImageMethod];

在我的连接类委托中,我可以看到我获取了数据,但我只是不知道如何将其传递回调用类。我的connectionDidFinishLoading方法直接来自Apple文档:

- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
    // do something with the data
    // receivedData is declared as a method instance elsewhere
    NSLog(@"Succeeded! Received %d bytes of data",[receivedData length]);

    // release the connection, and the data object
    [connection release];
    [receivedData release];
}

我希望这是一个简单的问题可以解决,但我担心我在这个问题上的知识有限,尽管进行了一些认真的谷歌搜索并尝试了许多不同的推荐方法,但我仍在努力想出一个解决方案。

最终,我将为我的应用程序提供一个复杂的缓存机制,其中表视图在出去并从服务器获取图像之前检查本地计算机上的图像,并且可能有一个进度指示器,直到检索图像。现在,如果使用同步过程图像足够大,即使本地图像填充也可能会很缓慢。

任何和所有的帮助将非常感激。

解决方案更新

如果其他人需要类似的解决方案,感谢 Ben 的帮助,这就是我想出的解决方案(当然,一般会针对发布进行修改)。请记住,我还实现了图像的自定义缓存,并使我的图像加载类足够通用,可供我的应用程序中的各个位置用于调用图像。

在我的调用方法中,在我的例子中是表中的自定义单元格......

ImageLoaderClass * myLoader = [[[ImageLoaderClass alloc] init] autorelease];

    [myLoader fetchImageWithURL:@"/my/thumbnail/path/with/filename.png"
                     forMethod:@"myUniqueRef"
                        withId:1234
                   saveToCache:YES
                     cachePath:@"/path/to/my/custom/cache"];

这将创建 myLoader 类的实例并向其传递 4 个参数。我想要获取的图像的 URL、用于确定在设置通知观察者时调用哪个类的唯一引用、图像的 ID、是否要将图像保存到缓存以及路径到缓存。

我的 ImageLoaderClass 定义了上面调用的方法,我在其中设置从调用单元传递的内容:

-(void)fetchImageWithURL:(NSString *)imageURL 
           forMethod:(NSString *)methodPassed 
              withId:(int)imageIdPassed 
         saveToCache:(BOOL)shouldISaveThis
           cachePath:(NSString *)cachePathToUse
{

    NSURLRequest *theRequest=[NSURLRequest requestWithURL:[NSURL URLWithString:imageURL]
                                          cachePolicy:NSURLRequestUseProtocolCachePolicy
                                      timeoutInterval:60.0];

    // Create the connection with the request and start loading the data

    NSURLConnection *theConnection=[[NSURLConnection alloc] initWithRequest:theRequest delegate:self];

    if (theConnection) {

        // Create the NSMutableData that will hold
        // the received data 
        // receivedData is declared as a method instance elsewhere

        receivedData = [[NSMutableData data] retain];

        // Now set the variables from the calling class

        [self setCallingMethod:methodPassed];
        [self setImageId:imageIdPassed];
        [self setSaveImage:shouldISaveThis];
        [self setImageCachePath:cachePathToUse];

    } else {
        // Do something to tell the user the image could not be downloaded
    }
}

In the connectionDidFinishLoading方法我将文件保存到缓存(如果需要)并向任何侦听观察者发出通知调用:

- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
    NSLog(@"Succeeded! Received %d bytes of data",[receivedData length]);

    // Create an image representation to use if not saving to cache
    // And create a dictionary to send with the notification    

    NSImage * mImage = [[NSImage alloc ] initWithData:receivedData];
    NSMutableDictionary * mDict = [[NSMutableDictionary alloc] init];

    // Add the ID into the dictionary so we can reference it if needed

    [mDict setObject:[NSNumber numberWithInteger:imageId] forKey:@"imageId"];

    if (saveImage)
    {
        // We just need to add the image to the dictionary and return it
        // because we aren't saving it to the custom cache

        // Put the mutable data into NSData so we can write it out

        NSData * dataToSave = [[NSData alloc] initWithData:receivedData];

        if (![dataToSave writeToFile:imageCachePath atomically:NO])
    NSLog(@"An error occured writing out the file");
    }
    else
    {
        // Save the image to the custom cache

        [mDict setObject:mImage forKey:@"image"];
    }

    // Now send the notification with the dictionary

    NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];

    [nc postNotificationName:callingMethod object:self userInfo:mDict];

    // And do some memory management cleanup

    [mImage release];
    [mDict release];
    [connection release];
    [receivedData release];
}

最后在表控制器中设置一个观察者来侦听通知并将其发送到处理重新显示自定义单元格的方法:

-(id)init
{
    [super init];

    NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];

    [nc addObserver:self selector:@selector(updateCellData:) name:@"myUniqueRef" object:nil];

    return self;
}

问题解决了!


我的解决方案是使用 Grand Central Dispatch (GCD) 来实现此目的,您可以在从服务器获取图像后将图像保存到光盘中。

- (NSView *)tableView:(NSTableView *)_tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
{
    SomeItem *item = [self.items objectAtIndex:row];
    NSTableCellView *cell = [_tableView makeViewWithIdentifier:tableColumn.identifier owner:self];
    if (item.artworkUrl)
    {
        cell.imageView.image = nil;
        dispatch_async(dispatch_queue_create("getAsynchronIconsGDQueue", NULL), 
        ^{
            NSURL *url = [NSURL URLWithString:item.artworkUrl];
            NSImage *image = [[NSImage alloc] initWithContentsOfURL:url];
            cell.imageView.image = image;        
        });
    }
    else
    {
        cell.imageView.image = nil;    
    }
    return cell;
}

(我正在使用自动引用计数(ARC),因此没有保留和释放。)

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

使用异步 NSURLConnection 中的数据填充 NSImage 的相关文章

随机推荐