在我的应用程序中,我有一个tableView,其中包含来自服务器的内容。要下载这些内容,我使用NSURLConnection并创建一个NSMutableArray(tableItems)来保存和管理我想要使用的图像的地址。
在connectionDidFinishLoading中,在填充tableItems之后,有这个for循环:
for (int i = 0; i < [tableItems count]; i++) {
// HERE I CHECK IF THE IMAGE I'M LOOKING FOR IS ALREADY ON DISK
NSString *pathToCheckImage = [NSString stringWithFormat:@"%@/%@.png", pathToPreviews, [tableItems objectAtIndex:i]];
NSData *dataOfCheckImage = [NSData dataWithContentsOfFile:pathToCheckImage];
UIImage *checkImage = [UIImage imageWithData:dataOfCheckImage];
NSString *pathToImage;
UIImage *image;
if (checkImage != nil) {
// THE IMAGE IS ALREADY ON DISK SO I GET IT FROM TEMP FOLDER
image = checkImage;
} else {
// THE IMAGE IS NEW SO I HAVE TO GET THE IMAGE FROM THE SERVER
pathToImage = [NSString stringWithFormat:@"http://www.SERVER.com/SERVER_PATH/%@.png", [tableItems objectAtIndex:i]];
NSURL *url = [NSURL URLWithString:pathToImage];
NSData *data = [NSData dataWithContentsOfURL:url];
image = [UIImage imageWithData:data];
// AND SAVE IT ON DISK
NSString *path = [NSString stringWithFormat:@"%@/%@.png", pathToPreviews, [tableItems objectAtIndex:i]];
[self cacheImageOnDisk:image withPath:path];
}
if (image != nil) {
[arrayOfImages addObject:image];
}
}
此代码正常工作,即使根据我必须从服务器下载的图像的数量和大小,执行任务可能需要1或2分钟。
问题是如果用户退出(按下主页按钮),而这个for循环正在运行,它会一直执行任务直到结束,即使它需要1分钟才能完成。
在此期间,再次启动应用程序不可避免地会在启动时崩溃。
我试图在退出时停止这个for循环,但是在for循环结束任务之前,不会调用applicationDidEnterBackground,applicationWillResignActive和applicationWillTerminate。
我试图设置“应用程序不在后台运行”,但没有任何改变。
任何建议都会非常感激。
答案 0 :(得分:4)
您不应该在主线程上下载图像。该应用程序将无法响应。 Grand Central Dispatch是完成这些任务的简单方法。
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSUInteger count = [tableItems count];
for (int i = 0; i < count; i++) {
// TODO: check if app didResignActive and break
UIImage *image = ... // get image from disk or network
// Add the image to the array on the main thread to avoid any threading issues
dispatch_sync(dispatch_get_main_queue(), ^{
[arrayOfImages addObject:image];
});
}
});
答案 1 :(得分:1)
你需要从主线程处理循环(很容易出错),或者将你的工作分解成块(更简单)。尝试将循环提取到一个运行10次的单独方法中,然后使用[... performSelector:@selector(myMethod)afterDelay:0]调度另一个运行;这将使runloop有机会每10次迭代循环和处理事件(如退出)。
线程(无论是通过较旧的方式还是较新的dispatch_async)仍然可以让你获得更好的响应能力,所以如果你想这样做,我的推荐就是这样:
在后台线程和主线程工作时共享无数据。
生成后台线程,完全使用其他地方无法访问的本地状态,然后使用完成的数组调回主线程。任何共享状态都很可能会让你的生活变得非常困难。