我编写了一个自定义视图控制器类,用于显示带注释的地图。按下注释时,将显示标注,并在注释标注的左侧显示缩略图图像。该类要求代理提供显示的图像。
- (void)mapView:(MKMapView *)mapView didSelectAnnotationView:(MKAnnotationView *)view
{
[(UIImageView *)view.leftCalloutAccessoryView setImage:[self.delegate mapViewController:self imageForAnnotation:view.annotation]];
}
委托类从网络中检索图像。为了防止UI无响应,创建了一个新线程以使用GCD下载图像。
- (UIImage *)mapViewController:(MapViewController *)sender imageForAnnotation:(id<MKAnnotation>)annotation
{
NSURL *someURL = [[NSURL alloc] initWithString:@"a URL to data on a network"];
__block UIImage *image = [[UIImage alloc] init];
dispatch_queue_t downloader = dispatch_queue_create("image downloader", NULL);
dispatch_async(downloader, ^{
NSData *imageData = [NSData dataWithContentsOfURL:someURL]; // This call can block the main UI!
image = [UIImage imageWithData:imageData];
});
return image;
}
为什么图像永远不会显示?我假设,因为委托方法返回一个指向图像的指针,该图像在将来的某个时间被设置为有效的图像数据,缩略图最终将自行更新。显然事实并非如此......
答案 0 :(得分:3)
您正在创建一个异步执行的块。这意味着您的代码创建了要执行的块,然后立即返回'image',它指向您初始化变量时创建的新图像:__block UIImage *image = [[UIImage alloc] init];
请记住,当您从方法返回一个对象时,实际上只是返回一个指向该对象的指针。
返回指向此新图像的指针后的某个时间。该块运行并将指向其检索到的图像的指针分配给局部变量“image”,该变量现在超出了方法的范围(尽管块仍然具有它)。所以现在该块对它所获得的图像有这个引用,但是当块完成时该引用会消失。
解决这个问题的一种方法是同步运行块,但这将破坏调度图像检索过程的重点。您需要做的是为检索图像后可以调用的函数提供一个块,即将图像分配到需要的位置。这看起来像这样:
- (void)mapViewController:(MapViewController *)sender imageForAnnotation:(id<MKAnnotation>)annotation withImageBlock:(void (^)(UIImage *))block{
{
NSURL *someURL = [[NSURL alloc] initWithString:@"a URL to data on a network"];
__block UIImage *image = [[UIImage alloc] init];
dispatch__object_t currentContext = dispatch_get_current_queue();
dispatch_queue_t downloader = dispatch_queue_create("image downloader", NULL);
dispatch_async(downloader, ^{
NSData *imageData = [NSData dataWithContentsOfURL:someURL]; // This call can block the main UI!
image = [UIImage imageWithData:imageData];
dispatch_async(currentContext, ^{
block(image);
});
});
}
请注意我抓取当前队列上下文,以便我可以在给予我的同一个线程上执行给我的块。这非常重要,因为传递给我的块可能包含UIKit方法,只能在主线程上执行。
答案 1 :(得分:1)
不幸的是,你在这里尝试做的事情并不容易。你需要创建一个“未来”(http://en.wikipedia.org/wiki/Futures_and_promises)并返回它,ObjC / Cocoa没有内置的实现。
您可以在这里做的最好的事情是:a)让调用者在完成下载时运行的块中传递并更新UI,或者b)返回占位符图像和安排a阻止在完成下载后替换图像。这两者都需要在某种程度上重构您的代码。后者还要求您下载代码以了解如何更新UI,这在增加耦合方面有点不幸。