iOS下载并保存应用程序内的图像

是否有可能从网站下载图像,并永久保存在我的应用程序内? 我真的不知道,但它会为我的应用程序一个很好的function。

asynchronous下载的图像与caching

asynchronous下载的图像与caching

这里有一个可用于下载图像的回购

尽pipe这里的其他答案是可行的, 但它们确实不是应该用于生产代码的解决scheme 。 (至less不是没有修改)

问题

这些答案的问题是,如果它们按原样实现,并且不从后台线程调用,则会在下载和保存图像时阻塞主线程。 这很糟糕

如果主线程被阻塞,则在图像的下载/保存完成之前不会发生UI更新。 作为一个例子,假设你在你的应用程序中添加一个UIActivityIndi​​catorView来向用户显示下载仍在进行中(我将在整个这个答案中使用这个例子),下面是粗略的控制stream程:

  1. 负责开始下载的对象被加载。
  2. 告诉活动指示器开始animation。
  3. 使用+[NSData dataWithContentsOfURL:]启动同步下载过程
  4. 保存刚刚下载的数据(图片)。
  5. 告诉活动指示器停止animation。

现在,这似乎是合理的控制stream程,但它掩盖了一个关键问题。

在主(UI)线程上调用活动指示器的startAnimating方法时,直到下一次主运行循环更新时才会实际发生此事件的UI更新,这是第一个主要问题。

在此更新有机会发生之前,会触发下载,并且由于这是一个同步操作,所以会阻塞主线程,直到完成下载(保存具有相同的问题)。 这实际上会阻止活动指示器启动其animation。 之后,你调用活动指标的stopAnimating方法,并期望一切都很好,但事实并非如此。

在这一点上,你可能会发现自己想知道以下几点。

为什么我的活动指标不显示?

那么,像这样想。 你告诉指标开始,但是在下载开始之前没有机会。 下载完成后,告诉指示器停止animation。 由于主线程在整个操作过程中被阻塞,所以实际上看到的行为更多地沿着指示器开始的行,然后立即告诉它停止,即使中间有一个(可能)大的下载任务。

现在,在最好的情况下 ,所有这些都会导致糟糕的用户体验(仍然非常糟糕)。 即使你认为这不是什么大问题,因为你只是下载一个小图片,下载几乎是瞬间发生的,但情况并非总是如此。 有些用户的networking连接速度较慢,或者服务器端可能出错,导致立即下载。

在这两种情况下,应用程序将无法处理用户界面更新,甚至无法触摸事件,而您的下载任务则围绕拇指等待下载完成或服务器响应其请求。

这意味着,从主线程同步下载可以防止您可能实现任何事情向用户表明正在进行下载。 而且由于触摸事件也在主线程上处理,所以这也抛出了添加任何types的取消button的可能性。

然后在最坏的情况下 ,你会开始接收崩溃报告,说明以下内容。

exceptiontypes:00000020exception代码:0x8badf00d

这些通过exception代码0x8badf00d很容易识别,可以看作是“吃不好的食物”。 看门狗定时器抛出了这个exception,它的工作是监视长时间运行的任务,阻塞主线程,如果这个过程持续太久,就会杀死这个有问题的应用程序。 可以说,这仍然是一个糟糕的用户体验问题,但是如果这种情况开始出现,应用已经越过了用户体验不佳和用户体验糟糕的界限。

下面是关于苹果关于同步networking技术问答的一些更多信息(为了简洁起见,缩写)。

在networking应用程序中看门狗超时崩溃的最常见原因是主线程上的同步networking。 这里有四个因素:

  1. 同步networking – 这是您发出networking请求并阻止等待响应的地方。
  2. 主线程 – 同步networking通常不太理想,但是如果在主线程中执行,会导致特定的问题。 请记住,主线程负责运行用户界面。 如果您在大量时间内阻塞主线程,用户界面会变得无法接受。
  3. 长时间的超时 – 如果networking刚刚消失(例如,用户正在进入隧道的火车上),任何未决的networking请求将不会失败,直到某个超时过期….

  1. 看门狗 – 为了保持用户界面的响应,iOS包括一个看门狗机制。 如果您的应用程序无法及时响应某些用户界面事件(启动,暂停,恢复,终止),则看门狗将终止您的应用程序,并生成看门狗超时崩溃报告。 看门狗给你的时间没有正式logging,但总是less于networking超时。

这个问题的一个棘手的方面是,它高度依赖于networking环境。 如果你总是在你的办公室testing你的应用程序,那么networking连接是好的,你永远不会看到这种types的崩溃。 但是,一旦您开始将应用程序部署到最终用户(将在各种networking环境中运行),这样的崩溃将变得很普遍。

现在,在这一点上,我将不再喋喋不休地解释为什么提供的答案可能有问题,并将开始提供一些替代解决scheme。 请记住,在这些示例中,我使用了小图片的URL,在使用更高分辨率的图片时您会注意到更大的差异。


解决scheme

我将从显示其他答案的安全版本开始,增加如何处理UI更新。 这将是几个例子中的第一个,所有这些例子都假设它们实现的类具有UIImageView的有效属性,UIActivityIndi​​catorView以及用于访问文档目录的documentsDirectoryURL方法。 在生产代码中,你可能想要实现你自己的方法来访问文件目录作为NSURL的一个类别,以获得更好的代码重用性,但是对于这些例子来说,这样可以。

 - (NSURL *)documentsDirectoryURL { NSError *error = nil; NSURL *url = [[NSFileManager defaultManager] URLForDirectory:NSDocumentDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:NO error:&error]; if (error) { // Figure out what went wrong and handle the error. } return url; } 

这些例子也会假设它们开始的线程是主线程。 这可能是默认行为,除非您从其他某个asynchronous任务的callback块开始执行下载任务。 如果你在一个典型的地方开始下载,比如视图控制器的生命周期方法(例如viewDidLoad,viewWillAppear :,等等),这将产生预期的行为。

这第一个例子将使用+[NSData dataWithContentsOfURL:]方法,但有一些关键的区别。 首先,你会注意到,在这个例子中,我们做的第一个调用是告诉活动指示器开始animation,然后在这个和同步的例子之间有一个直接的区别。 立即,我们使用dispatch_async(),传入全局并发队列将执行移动到后台线程。

在这一点上,你已经大大改善了你的下载任务。 由于dispatch_async()块中的所有内容现在都会在主线程中发生,因此您的界面将不再locking,并且您的应用程序将可以自由地响应触摸事件。

这里需要注意的是,这个块中的所有代码都将在后台线程上执行,直到图像的下载/保存成功为止,此时您可能想要告诉活动指示器stopAnimating ,或将新保存的图像应用于UIImageView。 无论哪种方式,这些都是UI的更新,这意味着你必须派遣主线程使用dispatch_get_main_queue()来执行它们。 否则会导致未定义的行为,这可能导致UI在意外的一段时间后更新,甚至可能导致崩溃。 在执行UI更新之前,务必确保您移回主线程。

 // Start the activity indicator before moving off the main thread [self.activityIndicator startAnimating]; // Move off the main thread to start our blocking tasks. dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ // Create the image URL from a known string. NSURL *imageURL = [NSURL URLWithString:@"http://www.google.comhttp://img.dovov.comsrpr/logo3w.png"]; NSError *downloadError = nil; // Create an NSData object from the contents of the given URL. NSData *imageData = [NSData dataWithContentsOfURL:imageURL options:kNilOptions error:&downloadError]; // ALWAYS utilize the error parameter! if (downloadError) { // Something went wrong downloading the image. Figure out what went wrong and handle the error. // Don't forget to return to the main thread if you plan on doing UI updates here as well. dispatch_async(dispatch_get_main_queue(), ^{ [self.activityIndicator stopAnimating]; NSLog(@"%@",[downloadError localizedDescription]); }); } else { // Get the path of the application's documents directory. NSURL *documentsDirectoryURL = [self documentsDirectoryURL]; // Append the desired file name to the documents directory path. NSURL *saveLocation = [documentsDirectoryURL URLByAppendingPathComponent:@"GCD.png"]; NSError *saveError = nil; BOOL writeWasSuccessful = [imageData writeToURL:saveLocation options:kNilOptions error:&saveError]; // Successful or not we need to stop the activity indicator, so switch back the the main thread. dispatch_async(dispatch_get_main_queue(), ^{ // Now that we're back on the main thread, you can make changes to the UI. // This is where you might display the saved image in some image view, or // stop the activity indicator. // Check if saving the file was successful, once again, utilizing the error parameter. if (writeWasSuccessful) { // Get the saved image data from the file. NSData *imageData = [NSData dataWithContentsOfURL:saveLocation]; // Set the imageView's image to the image we just saved. self.imageView.image = [UIImage imageWithData:imageData]; } else { NSLog(@"%@",[saveError localizedDescription]); // Something went wrong saving the file. Figure out what went wrong and handle the error. } [self.activityIndicator stopAnimating]; }); } }); 

现在请记住, 上面所示方法仍然不是一个理想的解决scheme,因为它不能被提前取消,它不会提供下载进度的指示,它不能处理任何types的身份validation挑战,它可以没有给出特定的超时间隔等(很多原因)。 我将介绍下面几个更好的选项。

在这些例子中,我只会介绍面向iOS 7的应用程序的解决scheme,并且正在考虑(撰写本文时)iOS 8是当前的主要版本, Applebuild议仅​​支持版本N和N-1 。 如果您需要支持较旧的iOS版本,我build议您查看NSURLConnection类以及AFNetworking的1.0版本。 如果您查看此答案的修订历史logging,可以使用NSURLConnection和ASIHTTPRequest查找基本示例,但应该注意,ASIHTTPRequest不再被维护, 应该用于新项目。


NSURLSession

让我们从iOS 7中引入的NSURLSession开始,极大地提高了在iOS中完成networking连接的难度 。 使用NSURLSession,您可以轻松地使用callback块执行asynchronousHTTP请求,并使用其委托处理身份validation挑战。 但是,这个类真正特别的地方在于,它也允许下载任务继续运行,即使应用程序被发送到后台,被终止,甚至崩溃。 这是一个使用的基本例子。

 // Start the activity indicator before starting the download task. [self.activityIndicator startAnimating]; NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration]; // Use a session with a custom configuration NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration]; // Create the image URL from some known string. NSURL *imageURL = [NSURL URLWithString:@"http://www.google.comhttp://img.dovov.comsrpr/logo3w.png"]; // Create the download task passing in the URL of the image. NSURLSessionDownloadTask *task = [session downloadTaskWithURL:imageURL completionHandler:^(NSURL *location, NSURLResponse *response, NSError *error) { // Get information about the response if neccessary. if (error) { NSLog(@"%@",[error localizedDescription]); // Something went wrong downloading the image. Figure out what went wrong and handle the error. // Don't forget to return to the main thread if you plan on doing UI updates here as well. dispatch_async(dispatch_get_main_queue(), ^{ [self.activityIndicator stopAnimating]; }); } else { NSError *openDataError = nil; NSData *downloadedData = [NSData dataWithContentsOfURL:location options:kNilOptions error:&openDataError]; if (openDataError) { // Something went wrong opening the downloaded data. Figure out what went wrong and handle the error. // Don't forget to return to the main thread if you plan on doing UI updates here as well. dispatch_async(dispatch_get_main_queue(), ^{ NSLog(@"%@",[openDataError localizedDescription]); [self.activityIndicator stopAnimating]; }); } else { // Get the path of the application's documents directory. NSURL *documentsDirectoryURL = [self documentsDirectoryURL]; // Append the desired file name to the documents directory path. NSURL *saveLocation = [documentsDirectoryURL URLByAppendingPathComponent:@"NSURLSession.png"]; NSError *saveError = nil; BOOL writeWasSuccessful = [downloadedData writeToURL:saveLocation options:kNilOptions error:&saveError]; // Successful or not we need to stop the activity indicator, so switch back the the main thread. dispatch_async(dispatch_get_main_queue(), ^{ // Now that we're back on the main thread, you can make changes to the UI. // This is where you might display the saved image in some image view, or // stop the activity indicator. // Check if saving the file was successful, once again, utilizing the error parameter. if (writeWasSuccessful) { // Get the saved image data from the file. NSData *imageData = [NSData dataWithContentsOfURL:saveLocation]; // Set the imageView's image to the image we just saved. self.imageView.image = [UIImage imageWithData:imageData]; } else { NSLog(@"%@",[saveError localizedDescription]); // Something went wrong saving the file. Figure out what went wrong and handle the error. } [self.activityIndicator stopAnimating]; }); } } }]; // Tell the download task to resume (start). [task resume]; 

从这里你会注意到downloadTaskWithURL: completionHandler:方法返回一个NSURLSessionDownloadTask的实例,在这个实例上调用一个实例方法-[NSURLSessionTask resume] 。 这是实际告诉下载任务启动的方法。 这意味着你可以启动你的下载任务,如果需要的话,可以暂停启动它(如果需要的话)。 这也意味着只要你存储了对任务的引用,你也可以利用它的cancelsuspend方法来取消或暂停任务,如果需要的话。

NSURLSessionTasks最棒的地方在于,只需要一点KVO ,你就可以监视它的countOfBytesExpectedToReceive和countOfBytesReceived属性的值,将这些值提供给一个NSByteCountFormatter ,并且用人类可读的单位轻松地为你的用户创build一个下载进度指示器(例如42 KB为100 KB)。

然而,在我离开NSURLSession之前,我想指出的是,可以避免在下载的callback块中的多个不同点处将dispatch_async重新分配回主线程的丑陋。 如果您select走这条路线,您可以使用初始化程序初始化会话,以便指定委托以及委托队列。 这将需要您使用委托模式,而不是callback块,但这可能是有益的,因为它是支持后台下载的唯一方法。

 NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration delegate:self delegateQueue:[NSOperationQueue mainQueue]]; 

AFNetworking 2.0

如果你从来没有听说过AFNetworking ,它是恕我直言,最后所有的networking库。 它是为Objective-C创build的,但它也适用于Swift。 用其作者的话来说:

AFNetworking是一个适用于iOS和Mac OS X的令人愉快的networking库。它build立在Foundation URL加载系统之上,扩展了Cocoa内置的强大的高级networking抽象。 它具有模块化的体系结构,devise精良,function丰富的API,使用起来非常愉快。

AFNetworking 2.0支持iOS 6及更高版本,但在本例中,我将使用AFHTTPSessionManager类,它需要使用iOS 7及更高版本,因为它使用NSURLSession类中的所有新API。 当你阅读下面的例子时,这将变得很明显,它与上面的NSURLSession例子共享了很多代码。

我想指出一些差异。 首先,不是创build自己的NSURLSession,而是创build一个AFURLSessionManager实例,它将在内部pipe理一个NSURLSession。 这样做可以让你利用一些便利的方法,如-[AFURLSessionManager downloadTaskWithRequest:progress:destination:completionHandler:] 。 这个方法的有趣之处在于,它可以让你用一个给定的目标文件path,一个完成块和一个NSProgress指针的input来相当简洁地创build一个下载任务,在这个input上你可以观察到关于下载进度的信息。 这是一个例子。

 // Use the default session configuration for the manager (background downloads must use the delegate APIs) NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration]; // Use AFNetworking's NSURLSessionManager to manage a NSURLSession. AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:configuration]; // Create the image URL from some known string. NSURL *imageURL = [NSURL URLWithString:@"http://www.google.comhttp://img.dovov.comsrpr/logo3w.png"]; // Create a request object for the given URL. NSURLRequest *request = [NSURLRequest requestWithURL:imageURL]; // Create a pointer for a NSProgress object to be used to determining download progress. NSProgress *progress = nil; // Create the callback block responsible for determining the location to save the downloaded file to. NSURL *(^destinationBlock)(NSURL *targetPath, NSURLResponse *response) = ^NSURL *(NSURL *targetPath, NSURLResponse *response) { // Get the path of the application's documents directory. NSURL *documentsDirectoryURL = [self documentsDirectoryURL]; NSURL *saveLocation = nil; // Check if the response contains a suggested file name if (response.suggestedFilename) { // Append the suggested file name to the documents directory path. saveLocation = [documentsDirectoryURL URLByAppendingPathComponent:response.suggestedFilename]; } else { // Append the desired file name to the documents directory path. saveLocation = [documentsDirectoryURL URLByAppendingPathComponent:@"AFNetworking.png"]; } return saveLocation; }; // Create the completion block that will be called when the image is done downloading/saving. void (^completionBlock)(NSURLResponse *response, NSURL *filePath, NSError *error) = ^void (NSURLResponse *response, NSURL *filePath, NSError *error) { dispatch_async(dispatch_get_main_queue(), ^{ // There is no longer any reason to observe progress, the download has finished or cancelled. [progress removeObserver:self forKeyPath:NSStringFromSelector(@selector(fractionCompleted))]; if (error) { NSLog(@"%@",error.localizedDescription); // Something went wrong downloading or saving the file. Figure out what went wrong and handle the error. } else { // Get the data for the image we just saved. NSData *imageData = [NSData dataWithContentsOfURL:filePath]; // Get a UIImage object from the image data. self.imageView.image = [UIImage imageWithData:imageData]; } }); }; // Create the download task for the image. NSURLSessionDownloadTask *task = [manager downloadTaskWithRequest:request progress:&progress destination:destinationBlock completionHandler:completionBlock]; // Start the download task. [task resume]; // Begin observing changes to the download task's progress to display to the user. [progress addObserver:self forKeyPath:NSStringFromSelector(@selector(fractionCompleted)) options:NSKeyValueObservingOptionNew context:NULL]; 

当然,因为我们已经将包含这个代码的类作为观察者添加到了NSProgress实例的一个属性中,所以您必须实现-[NSObject observeValueForKeyPath:ofObject:change:context:]方法。 在这种情况下,我已经包含了一个如何更新进度标签来显示下载进度的例子。 这很容易。 NSProgress有一个实例方法localizedDescription ,它将以本地化的,可读的格式显示进度信息。

 - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { // We only care about updates to fractionCompleted if ([keyPath isEqualToString:NSStringFromSelector(@selector(fractionCompleted))]) { NSProgress *progress = (NSProgress *)object; // localizedDescription gives a string appropriate for display to the user, ie "42% completed" self.progressLabel.text = progress.localizedDescription; } else { [super observeValueForKeyPath:keyPath ofObject:object change:change context:context]; } } 

不要忘了,如果你想在你的项目中使用AFNetworking,你需要遵循它的安装说明,并确保#import <AFNetworking/AFNetworking.h>

Alamofire

最后,我想用Alamofire给出最后一个例子。 这是一个使Swiftnetworking化的图书馆。 对于这个例子的内容,我没有特别的细节,但是它和最后一个例子几乎是一样的,只是以一种更可爱的方式。

 // Create the destination closure to pass to the download request. I haven't done anything with them // here but you can utilize the parameters to make adjustments to the file name if neccessary. let destination = { (url: NSURL!, response: NSHTTPURLResponse!) -> NSURL in var error: NSError? // Get the documents directory let documentsDirectory = NSFileManager.defaultManager().URLForDirectory(.DocumentDirectory, inDomain: .UserDomainMask, appropriateForURL: nil, create: false, error: &error ) if let error = error { // This could be bad. Make sure you have a backup plan for where to save the image. println("\(error.localizedDescription)") } // Return a destination of .../Documents/Alamofire.png return documentsDirectory!.URLByAppendingPathComponent("Alamofire.png") } Alamofire.download(.GET, "http://www.google.comhttp://img.dovov.comsrpr/logo3w.png", destination) .validate(statusCode: 200..<299) // Require the HTTP status code to be in the Successful range. .validate(contentType: ["image/png"]) // Require the content type to be image/png. .progress { (bytesRead, totalBytesRead, totalBytesExpectedToRead) in // Create an NSProgress object to represent the progress of the download for the user. let progress = NSProgress(totalUnitCount: totalBytesExpectedToRead) progress.completedUnitCount = totalBytesRead dispatch_async(dispatch_get_main_queue()) { // Move back to the main thread and update some progress label to show the user the download is in progress. self.progressLabel.text = progress.localizedDescription } } .response { (request, response, _, error) in if error != nil { // Something went wrong. Handle the error. } else { // Open the newly saved image data. if let imageData = NSData(contentsOfURL: destination(nil, nil)) { dispatch_async(dispatch_get_main_queue()) { // Move back to the main thread and add the image to your image view. self.imageView.image = UIImage(data: imageData) } } } } 

你不能在应用程序包中保存任何内容,但是你可以使用+[NSData dataWithContentsOfURL:]将图像存储在应用程序的文档目录中,例如:

 NSData *imageData = [NSData dataWithContentsOfURL:myImageURL]; NSString *imagePath = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0] stringByAppendingPathComponent:@"/myImage.png"]; [imageData writeToFile:imagePath atomically:YES]; 

不完全是永久性的 ,但至less在用户删除应用程序之前,它一直保持在那里。

这是主要的概念。 玩的开心 ;)

 NSURL *url = [NSURL URLWithString:@"http://example.com/yourImage.png"]; NSData *data = [NSData dataWithContentsOfURL:url]; NSString *path = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0]; path = [path stringByAppendingString:@"/yourLocalImage.png"]; [data writeToFile:path atomically:YES]; 

由于我们现在在IO5上,所以您不再需要将映像写入磁盘了。
您现在可以在coredata二进制属性上设置“允许外部存储”。 根据苹果发行说明,这意味着以下内容:

像图像缩略图这样的小数据值可以高效地存储在数据库中,但大型照片或其他媒体最好由文件系统直接处理。 您现在可以指定被pipe理对象属性的值可以作为外部logging存储 – 请参阅setAllowsExternalBinaryDataStorage:启用时,核心数据试探性地决定是否应将数据直接保存在数据库中或存储URI到一个单独的文件,它为你pipe理。 如果使用此选项,则无法基于二进制数据属性的内容进行查询。

正如其他人所说,在很多情况下,您应该在后台线程中下载图片而不会阻塞用户界面

在这种情况下,我最喜欢的解决scheme是使用一个方便的方法块,就像这样:(信用 – > iOS:如何asynchronous下载图像(并使您的UITableView滚动快) )

 - (void)downloadImageWithURL:(NSURL *)url completionBlock:(void (^)(BOOL succeeded, UIImage *image))completionBlock { NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; [NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) { if ( !error ) { UIImage *image = [[UIImage alloc] initWithData:data]; completionBlock(YES,image); } else{ completionBlock(NO,nil); } }]; } 

并称之为

 NSURL *imageUrl = //... [[MyUtilManager sharedInstance] downloadImageWithURL:[NSURL URLWithString:imageURL] completionBlock:^(BOOL succeeded, UIImage *image) { //Here you can save the image permanently, update UI and do what you want... }]; 

以下是我如何下载广告横幅 如果您正在下载大图像或一堆图像,最好在后台执行此操作。

 - (void)viewDidLoad { [super viewDidLoad]; [self performSelectorInBackground:@selector(loadImageIntoMemory) withObject:nil]; } - (void)loadImageIntoMemory { NSString *temp_Image_String = [[NSString alloc] initWithFormat:@"http://yourwebsite.com/MyImageName.jpg"]; NSURL *url_For_Ad_Image = [[NSURL alloc] initWithString:temp_Image_String]; NSData *data_For_Ad_Image = [[NSData alloc] initWithContentsOfURL:url_For_Ad_Image]; UIImage *temp_Ad_Image = [[UIImage alloc] initWithData:data_For_Ad_Image]; [self saveImage:temp_Ad_Image]; UIImageView *imageViewForAdImages = [[UIImageView alloc] init]; imageViewForAdImages.frame = CGRectMake(0, 0, 320, 50); imageViewForAdImages.image = [self loadImage]; [self.view addSubview:imageViewForAdImages]; } - (void)saveImage: (UIImage*)image { NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); NSString *documentsDirectory = [paths objectAtIndex:0]; NSString* path = [documentsDirectory stringByAppendingPathComponent: @"MyImageName.jpg" ]; NSData* data = UIImagePNGRepresentation(image); [data writeToFile:path atomically:YES]; } - (UIImage*)loadImage { NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); NSString *documentsDirectory = [paths objectAtIndex:0]; NSString* path = [documentsDirectory stringByAppendingPathComponent:@"MyImageName.jpg" ]; UIImage* image = [UIImage imageWithContentsOfFile:path]; return image; } 

这里是代码从url下载图像asynchronous,然后保存在Objective-C: – >的位置

  + (void)downloadImageWithURL:(NSURL *)url completionBlock:(void (^)(BOOL succeeded, UIImage *image))completionBlock { NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; [NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) { if ( !error ) { UIImage *image = [[UIImage alloc] initWithData:data]; completionBlock(YES,image); } else{ completionBlock(NO,nil); } }]; } 

如果您正在使用AFNetworking库下载图像,并在UITableview中使用图像,那么您可以使用cellForRowAtIndexPath

  [self setImageWithURL:user.user_ProfilePicturePath toControl:cell.imgView]; -(void)setImageWithURL:(NSURL*)url toControl:(id)ctrl { NSURLRequest *request = [NSURLRequest requestWithURL:url]; AFImageRequestOperation *operation = [AFImageRequestOperation imageRequestOperationWithRequest:request imageProcessingBlock:nil success:^(NSURLRequest *request, NSHTTPURLResponse *response, UIImage *image) { if (image) { if([ctrl isKindOfClass:[UIButton class]]) { UIButton btn =(UIButton )ctrl; [btn setBackgroundImage:image forState:UIControlStateNormal]; } else { UIImageView imgView = (UIImageView )ctrl; imgView.image = image; } } } failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error) { NSLog(@"No Image"); }]; [operation start];} 

[self setImageWithURL:user.user_ProfilePicturePath toControl:cell.imgView]; -(void)setImageWithURL:(NSURL*)url toControl:(id)ctrl { NSURLRequest *request = [NSURLRequest requestWithURL:url]; AFImageRequestOperation *operation = [AFImageRequestOperation imageRequestOperationWithRequest:request imageProcessingBlock:nil success:^(NSURLRequest *request, NSHTTPURLResponse *response, UIImage *image) { if (image) { if([ctrl isKindOfClass:[UIButton class]]) { UIButton btn =(UIButton )ctrl; [btn setBackgroundImage:image forState:UIControlStateNormal]; } else { UIImageView imgView = (UIImageView )ctrl; imgView.image = image; } } } failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error) { NSLog(@"No Image"); }]; [operation start];}

您可以使用NSURLSessionDataTask下载图像而不阻塞UI。

 +(void)downloadImageWithURL:(NSURL *)url completionBlock:(void (^)(BOOL succeeded, UIImage *image))completionBlock { NSURLSessionDataTask* _sessionTask = [[NSURLSession sharedSession] dataTaskWithRequest:[NSURLRequest requestWithURL:url] completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { if (error != nil) { if ([error code] == NSURLErrorAppTransportSecurityRequiresSecureConnection) { completionBlock(NO,nil); } } else { [[NSOperationQueue mainQueue] addOperationWithBlock: ^{ dispatch_async(dispatch_get_main_queue(), ^{ UIImage *image = [[UIImage alloc] initWithData:data]; completionBlock(YES,image); }); }]; } }]; [_sessionTask resume]; }