2012-02-04 39 views
0

在我的应用程序中,我有一个tableView充满了来自服务器的内容。要下载这些内容,我使用NSURLConnection,并创建一个NSMutableArray(tableItems)来保存和管理我想要使用的图像的地址。正在运行for循环时退出应用程序

在connectionDidFinishLoading,填充tableItems后,有这样的循环:

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分钟才能完成任务。

问题是,如果用户退出(home按钮被按下),而这个for-loop正在运行,它会继续执行它的任务直到结束,即使它需要1分钟才能完成它。

在此期间,再次启动应用程序不可避免地会在启动时崩溃。

我试图阻止这个for-loop退出,但applicationDidEnterBackground,applicationWillResignActive和applicationWillTerminate不会被调用,直到for-loop结束它的任务。

我试着设置“应用程序不在后台运行”,但没有任何改变。

任何建议将非常感激。

+0

你可以拦截按钮按下,在某处设置一个标志,并检查你的循环中的标志,并保释,如果它的设置? – Bill 2012-02-04 01:53:30

+0

这就是我正在做的事情,但applicationDidEnterBackground,applicationWillResignActive和applicationWillTerminate不会被调用,直到for-loop结束它的任务,所以我不知道如何拦截按钮按下。我错过了一些基本的架构? – Beppe 2012-02-04 02:01:55

回答

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]; 
     }); 
    } 
}); 
+0

你的代码完美无缺!感谢你的分享。 – Beppe 2012-02-04 15:40:43

1

你需要处理你的循环离开主线程(很容易出错),或者把你的工作分解成块(更简单)。尝试将你的循环提取到一个单独的方法中,运行它10次,然后使用[... performSelector:@selector(myMethod)afterDelay:0]安排另一次运行;这将使runloop有机会循环和处理每10次迭代的事件(如退出)。

线程(无论是通过老的方法或较新的dispatch_async)仍然会得到你更好的响应性的,所以,如果你想这样做,我的建议是这样的:

分享后台线程和主之间没有数据线程正在工作。

产生你的后台线程,完全用本地状态完成你的工作,而不是在其他任何地方访问,然后用你已完成的数组发送回主线程。任何共享状态都很可能让你的生活变得非常困难。

+0

Jason的代码完美工作,然后我选择他的答案,但为线索提示+1,并提出一个简单的解决方法,比如将我的工作分解为块(这让我想到很多可能的方法)。感谢您的帮助!!! – Beppe 2012-02-04 15:49:41

+1

贾森的代码是我的第二个建议的实现:)它看起来很好,但记住我的警告,如果你修改它。异步部分没有共享状态。 – 2012-02-05 03:30:05