网络文件下载(提供多种下载方式)

(1)使用 NSURLConnection 直接方式

(2)使用 NSURLConnection 代理方式

(3)使用 NSURLSession 直接方式

(4)使用 NSURLSession 代理方式

(5)使用 AFNetworking 方式

附加功能:

(1)使用 AFNetworking 中的 AFNetworkReachabilityManager 来检查网络情况:

  • AFNetworkReachabilityStatusReachableViaWiFi:Wi-Fi 网络下

  • AFNetworkReachabilityStatusReachableViaWWAN:2G/3G/4G 蜂窝移动网络下

  • AFNetworkReachabilityStatusNotReachable:未连接网络

(2)使用 AFNetworking 中的 AFNetworkActivityIndicatorManager 来启动网络活动指示器:

1 #import "AFNetworkActivityIndicatorManager.h"
2 
3 //启动网络活动指示器;会根据网络交互情况,实时显示或隐藏网络活动指示器;他通过「通知与消息机制」来实现 [UIApplication sharedApplication].networkActivityIndicatorVisible 的控制
4 [AFNetworkActivityIndicatorManager sharedManager].enabled = YES;

效果如下:

ViewController.h

1 #import <UIKit/UIKit.h>
2 
3 @interface ViewController : UITableViewController
4 @property (copy, nonatomic) NSArray *arrSampleName;
5 
6 - (instancetype)initWithSampleNameArray:(NSArray *)arrSampleName;
7 
8 @end 

ViewController.m

 1 #import "ViewController.h"
 2 #import "NSURLConnectionViewController.h"
 3 #import "NSURLConnectionDelegateViewController.h"
 4 #import "NSURLSessionViewController.h"
 5 #import "NSURLSessionDelegateViewController.h"
 6 #import "AFNetworkingViewController.h"
 7 
 8 @interface ViewController ()
 9 - (void)layoutUI;
10 @end
11 
12 @implementation ViewController
13 - (void)viewDidLoad {
14     [super viewDidLoad];
15     
16     [self layoutUI];
17 }
18 
19 - (void)didReceiveMemoryWarning {
20     [super didReceiveMemoryWarning];
21     // Dispose of any resources that can be recreated.
22 }
23 
24 - (instancetype)initWithSampleNameArray:(NSArray *)arrSampleName {
25     if (self = [super initWithStyle:UITableViewStyleGrouped]) {
26         self.navigationItem.title = @"多种方式实现文件下载功能";
27         self.navigationItem.backBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"返回" style:UIBarButtonItemStylePlain target:nil action:nil];
28         
29         _arrSampleName = arrSampleName;
30     }
31     return self;
32 }
33 
34 - (void)layoutUI {
35 }
36 
37 #pragma mark - UITableViewController相关方法重写
38 - (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
39     return 0.1;
40 }
41 
42 - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
43     return 1;
44 }
45 
46 - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
47     return [_arrSampleName count];
48 }
49 
50 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
51     static NSString *cellIdentifier = @"cell";
52     UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
53     if (!cell) {
54         cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
55     }
56     cell.textLabel.text = _arrSampleName[indexPath.row];
57     return cell;
58 }
59 
60 - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
61     switch (indexPath.row) {
62         case 0: {
63             NSURLConnectionViewController *connectionVC = [NSURLConnectionViewController new];
64             [self.navigationController pushViewController:connectionVC animated:YES];
65             break;
66         }
67         case 1: {
68             NSURLConnectionDelegateViewController *connectionDelegateVC = [NSURLConnectionDelegateViewController new];
69             [self.navigationController pushViewController:connectionDelegateVC animated:YES];
70             break;
71         }
72         case 2: {
73             NSURLSessionViewController *sessionVC = [NSURLSessionViewController new];
74             [self.navigationController pushViewController:sessionVC animated:YES];
75             break;
76         }
77         case 3: {
78             NSURLSessionDelegateViewController *sessionDelegateVC = [NSURLSessionDelegateViewController new];
79             [self.navigationController pushViewController:sessionDelegateVC animated:YES];
80             break;
81         }
82         case 4: {
83             AFNetworkingViewController *networkingVC = [AFNetworkingViewController new];
84             [self.navigationController pushViewController:networkingVC animated:YES];
85             break;
86         }
87         default:
88             break;
89     }
90 }
91 
92 @end 

PrefixHeader.pch

1 #define kFileURLStr @"http://files.cnblogs.com/files/huangjianwu/metro_demo使用Highcharts实现图表展示.zip"
2 
3 #define kTitleOfNSURLConnection @"使用 NSURLConnection 直接方式"
4 #define kTitleOfNSURLConnectionDelegate @"使用 NSURLConnection 代理方式"
5 #define kTitleOfNSURLSession @"使用 NSURLSession 直接方式"
6 #define kTitleOfNSURLSessionDelegate @"使用 NSURLSession 代理方式"
7 #define kTitleOfAFNetworking @"使用 AFNetworking 方式"
8 
9 #define kApplication [UIApplication sharedApplication] 

UIButton+BeautifulButton.h

 1 #import <UIKit/UIKit.h>
 2 
 3 @interface UIButton (BeautifulButton)
 4 /**
 5  *  根据按钮文字颜色,返回对应文字颜色的圆角按钮
 6  *
 7  *  @param tintColor 按钮文字颜色;nil 的话就为深灰色
 8  */
 9 - (void)beautifulButton:(UIColor *)tintColor;
10 
11 @end 

UIButton+BeautifulButton.m

 1 #import "UIButton+BeautifulButton.h"
 2 
 3 @implementation UIButton (BeautifulButton)
 4 
 5 - (void)beautifulButton:(UIColor *)tintColor {
 6     self.tintColor = tintColor ?: [UIColor darkGrayColor];
 7     self.layer.masksToBounds = YES;
 8     self.layer.cornerRadius = 10.0;
 9     self.layer.borderColor = [UIColor grayColor].CGColor;
10     self.layer.borderWidth = 1.0;
11 }
12 
13 @end 

NSURLConnectionViewController.h

1 #import <UIKit/UIKit.h>
2 
3 @interface NSURLConnectionViewController : UIViewController
4 @property (strong, nonatomic) IBOutlet UILabel *lblFileName;
5 @property (strong, nonatomic) IBOutlet UILabel *lblMessage;
6 @property (strong, nonatomic) IBOutlet UIButton *btnDownloadFile;
7 
8 @end 

NSURLConnectionViewController.m

 1 #import "NSURLConnectionViewController.h"
 2 #import "UIButton+BeautifulButton.h"
 3 
 4 @interface NSURLConnectionViewController ()
 5 - (void)layoutUI;
 6 - (void)saveDataToDisk:(NSData *)data;
 7 @end
 8 
 9 @implementation NSURLConnectionViewController
10 
11 - (void)viewDidLoad {
12     [super viewDidLoad];
13     
14     [self layoutUI];
15 }
16 
17 - (void)didReceiveMemoryWarning {
18     [super didReceiveMemoryWarning];
19     // Dispose of any resources that can be recreated.
20 }
21 
22 - (void)layoutUI {
23     self.navigationItem.title = kTitleOfNSURLConnection;
24     self.view.backgroundColor = [UIColor colorWithWhite:0.95 alpha:1.000];
25     
26     [_btnDownloadFile beautifulButton:nil];
27 }
28 
29 - (void)saveDataToDisk:(NSData *)data {
30     //数据接收完保存文件;注意苹果官方要求:下载数据只能保存在缓存目录(/Library/Caches)
31     NSString *savePath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
32     savePath = [savePath stringByAppendingPathComponent:_lblFileName.text];
33     [data writeToFile:savePath atomically:YES]; //writeToFile: 方法:如果 savePath 文件存在,他会执行覆盖
34 }
35 
36 - (IBAction)downloadFile:(id)sender {
37     _lblMessage.text = @"下载中...";
38     
39     NSString *fileURLStr = kFileURLStr;
40     //编码操作;对应的解码操作是用 stringByRemovingPercentEncoding 方法
41     fileURLStr = [fileURLStr stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
42     NSURL *fileURL = [NSURL URLWithString:fileURLStr];
43     
44     //创建请求
45     NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:fileURL];
46     
47     //创建连接;Apple 提供的处理一般请求的两种方法,他们不需要进行一系列的 NSURLConnectionDataDelegate 委托协议方法操作,简洁直观
48     //方法一:发送一个同步请求;不建议使用,因为当前线程是主线程的话,会造成线程阻塞,一般比较少用
49 //    NSURLResponse *response;
50 //    NSError *connectionError;
51 //    NSData *data = [NSURLConnection sendSynchronousRequest:request
52 //                                         returningResponse:&response
53 //                                                     error:&connectionError];
54 //    if (!connectionError) {
55 //        [self saveDataToDisk:data];
56 //        NSLog(@"保存成功");
57 //        
58 //        _lblMessage.text = @"下载完成";
59 //    } else {
60 //        NSLog(@"下载失败,错误信息:%@", connectionError.localizedDescription);
61 //        
62 //        _lblMessage.text = @"下载失败";
63 //    }
64     
65     //方法二:发送一个异步请求
66     [NSURLConnection sendAsynchronousRequest:request
67                                        queue:[NSOperationQueue mainQueue]
68                            completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
69                                if (!connectionError) {
70                                    [self saveDataToDisk:data];
71                                    NSLog(@"保存成功");
72                                    
73                                    _lblMessage.text = @"下载完成";
74                                    
75                                } else {
76                                    NSLog(@"下载失败,错误信息:%@", connectionError.localizedDescription);
77                                    
78                                    _lblMessage.text = @"下载失败";
79                                }
80                            }];
81 }
82 
83 @end 

NSURLConnectionViewController.xib

 View Code 

NSURLConnectionDelegateViewController.h

 1 #import <UIKit/UIKit.h>
 2 
 3 @interface NSURLConnectionDelegateViewController : UIViewController
 4 @property (strong, nonatomic) NSMutableData *mDataReceive;
 5 @property (assign, nonatomic) NSUInteger totalDataLength;
 6 
 7 @property (strong, nonatomic) IBOutlet UILabel *lblFileName;
 8 @property (strong, nonatomic) IBOutlet UIProgressView *progVDownloadFile;
 9 @property (strong, nonatomic) IBOutlet UILabel *lblMessage;
10 @property (strong, nonatomic) IBOutlet UIButton *btnDownloadFile;
11 
12 @end 

NSURLConnectionDelegateViewController.m

  1 #import "NSURLConnectionDelegateViewController.h"
  2 #import "UIButton+BeautifulButton.h"
  3 
  4 @interface NSURLConnectionDelegateViewController ()
  5 - (void)layoutUI;
  6 - (BOOL)isExistCacheInMemory:(NSURLRequest *)request;
  7 - (void)updateProgress;
  8 @end
  9 
 10 @implementation NSURLConnectionDelegateViewController
 11 
 12 - (void)viewDidLoad {
 13     [super viewDidLoad];
 14     
 15     [self layoutUI];
 16 }
 17 
 18 - (void)didReceiveMemoryWarning {
 19     [super didReceiveMemoryWarning];
 20     // Dispose of any resources that can be recreated.
 21 }
 22 
 23 - (void)layoutUI {
 24     self.navigationItem.title = kTitleOfNSURLConnectionDelegate;
 25     self.view.backgroundColor = [UIColor colorWithWhite:0.95 alpha:1.000];
 26     
 27     [_btnDownloadFile beautifulButton:nil];
 28 }
 29 
 30 - (BOOL)isExistCacheInMemory:(NSURLRequest *)request {
 31     BOOL isExistCache = NO;
 32     NSURLCache *cache = [NSURLCache sharedURLCache];
 33     [cache setMemoryCapacity:1024 * 1024]; //1M
 34     
 35     NSCachedURLResponse *response = [cache cachedResponseForRequest:request];
 36     if (response != nil) {
 37         NSLog(@"内存中存在对应请求的响应缓存");
 38         isExistCache = YES;
 39     }
 40     return isExistCache;
 41 }
 42 
 43 - (void)updateProgress {
 44     NSUInteger receiveDataLength = _mDataReceive.length;
 45     if (receiveDataLength == _totalDataLength) {
 46         _lblMessage.text = @"下载完成";
 47         kApplication.networkActivityIndicatorVisible = NO;
 48     } else {
 49         _lblMessage.text = @"下载中...";
 50         kApplication.networkActivityIndicatorVisible = YES;
 51         _progVDownloadFile.progress = (float)receiveDataLength / _totalDataLength;
 52     }
 53 }
 54 
 55 - (IBAction)downloadFile:(id)sender {
 56     /*
 57      此例子更多的是希望大家了解代理方法接收响应数据的过程,实际开发中也不可能使用这种方法进行文件下载。这种下载有个致命的问题:无法进行大文件下载。因为代理方法在接收数据时虽然表面看起来是每次读取一部分响应数据,事实上它只有一次请求并且也只接收了一次服务器响应,只是当响应数据较大时系统会重复调用数据接收方法,每次将已读取的数据拿出一部分交给数据接收方法而已。在这个过程中其实早已经将响应数据全部拿到,只是分批交给开发者而已。这样一来对于几个G的文件如果进行下载,那么不用说是真机下载了,就算是模拟器恐怕也是不现实的。
 58      实际开发文件下载的时候不管是通过代理方法还是静态方法执行请求和响应,我们都会分批请求数据,而不是一次性请求数据。假设一个文件有1G,那么只要每次请求1M的数据,请求1024次也就下载完了。那么如何让服务器每次只返回1M的数据呢?
 59      在网络开发中可以在请求的头文件中设置一个Range信息,它代表请求数据的大小。通过这个字段配合服务器端可以精确的控制每次服务器响应的数据范围。例如指定bytes=0-1023,然后在服务器端解析Range信息,返回该文件的0到1023之间的数据的数据即可(共1024Byte)。这样,只要在每次发送请求控制这个头文件信息就可以做到分批请求。
 60      当然,为了让整个数据保持完整,每次请求的数据都需要逐步追加直到整个文件请求完成。但是如何知道整个文件的大小?其实在此例子通过头文件信息获取整个文件大小,他请求整个数据,这样做对分段下载就没有任何意义了。所幸在WEB开发中我们还有另一种请求方法“HEAD”,通过这种请求服务器只会响应头信息,其他数据不会返回给客户端,这样一来整个数据的大小也就可以得到了。
 61      */
 62     
 63     
 64     NSString *fileURLStr = kFileURLStr;
 65     //编码操作;对应的解码操作是用 stringByRemovingPercentEncoding 方法
 66     fileURLStr = [fileURLStr stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
 67     NSURL *fileURL = [NSURL URLWithString:fileURLStr];
 68     
 69     /*创建请求
 70      cachePolicy:缓存策略
 71      1、NSURLRequestUseProtocolCachePolicy 协议缓存,根据 response 中的 Cache-Control 字段判断缓存是否有效,如果缓存有效则使用缓存数据否则重新从服务器请求
 72      2、NSURLRequestReloadIgnoringLocalCacheData 不使用缓存,直接请求新数据
 73      3、NSURLRequestReloadIgnoringCacheData 等同于 NSURLRequestReloadIgnoringLocalCacheData
 74      4、NSURLRequestReturnCacheDataElseLoad 直接使用缓存数据不管是否有效,没有缓存则重新请求
 75      5、NSURLRequestReturnCacheDataDontLoad 直接使用缓存数据不管是否有效,没有缓存数据则失败
 76      
 77      timeoutInterval:超时时间设置(默认60s)
 78      */
 79     NSURLRequest *request = [[NSURLRequest alloc] initWithURL:fileURL
 80                                                   cachePolicy:NSURLRequestUseProtocolCachePolicy
 81                                               timeoutInterval:60.0];
 82     if ([self isExistCacheInMemory:request]) {
 83         request = [[NSURLRequest alloc] initWithURL:fileURL
 84                                         cachePolicy:NSURLRequestReturnCacheDataDontLoad
 85                                     timeoutInterval:60.0];
 86     }
 87     
 88     //创建连接,异步操作
 89     NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request
 90                                                                   delegate:self];
 91     [connection start]; //启动连接
 92 }
 93 
 94 #pragma mark - NSURLConnectionDataDelegate
 95 - (NSURLRequest *)connection:(NSURLConnection *)connection willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)response {
 96     NSLog(@"即将发送请求");
 97     
 98     return request;
 99 }
100 
101 - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
102     NSLog(@"已经接收到响应");
103     
104     _mDataReceive = [NSMutableData new];
105     _progVDownloadFile.progress = 0.0;
106     
107     //通过响应头中的 Content-Length 获取到整个响应的总长度
108     /*
109      {
110      "Accept-Ranges" = bytes;
111      "Cache-Control" = "max-age=7776000";
112      "Content-Length" = 592441;
113      "Content-Type" = "application/x-zip-compressed";
114      Date = "Wed, 02 Sep 2015 13:17:01 GMT";
115      Etag = ""d8f617371f9cd01:0"";
116      "Last-Modified" = "Mon, 01 Jun 2015 03:58:27 GMT";
117      Server = "Microsoft-IIS/7.5";
118      "X-Powered-By" = "ASP.NET";
119      }
120      */
121     NSDictionary *dicHeaderField = [(NSHTTPURLResponse *)response allHeaderFields];
122     _totalDataLength = [[dicHeaderField objectForKey:@"Content-Length"] integerValue];
123 }
124 
125 - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
126     NSLog(@"已经接收到响应数据,数据长度为%lu字节...", (unsigned long)[data length]);
127     
128     [_mDataReceive appendData:data]; //连续接收数据
129     [self updateProgress]; //连续更新进度条
130 }
131 
132 - (void)connectionDidFinishLoading:(NSURLConnection *)connection {
133     NSLog(@"已经接收完所有响应数据");
134     
135     NSString *savePath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
136     savePath = [savePath stringByAppendingPathComponent:_lblFileName.text];
137     [_mDataReceive writeToFile:savePath atomically:YES];
138 }
139 
140 - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
141     //如果连接超时或者连接地址错误可能就会报错
142     NSLog(@"连接错误,错误信息:%@", error.localizedDescription);
143     
144     _lblMessage.text = @"连接错误";
145 }
146 
147 @end

NSURLConnectionDelegateViewController.xib

 View Code

NSURLSessionViewController.h

1 #import <UIKit/UIKit.h>
2 
3 @interface NSURLSessionViewController : UIViewController
4 @property (strong, nonatomic) IBOutlet UILabel *lblFileName;
5 @property (strong, nonatomic) IBOutlet UILabel *lblMessage;
6 @property (strong, nonatomic) IBOutlet UIButton *btnDownloadFile;
7 
8 @end 

NSURLSessionViewController.m

  1 #import "NSURLSessionViewController.h"
  2 #import "UIButton+BeautifulButton.h"
  3 
  4 @interface NSURLSessionViewController ()
  5 - (void)layoutUI;
  6 @end
  7 
  8 @implementation NSURLSessionViewController
  9 
 10 - (void)viewDidLoad {
 11     [super viewDidLoad];
 12     
 13     [self layoutUI];
 14 }
 15 
 16 - (void)didReceiveMemoryWarning {
 17     [super didReceiveMemoryWarning];
 18     // Dispose of any resources that can be recreated.
 19 }
 20 
 21 - (void)layoutUI {
 22     self.navigationItem.title = kTitleOfNSURLSession;
 23     self.view.backgroundColor = [UIColor colorWithWhite:0.95 alpha:1.000];
 24     
 25     [_btnDownloadFile beautifulButton:nil];
 26 }
 27 
 28 - (IBAction)downloadFile:(id)sender {
 29     _lblMessage.text = @"下载中...";
 30     
 31     NSString *fileURLStr = kFileURLStr;
 32     //编码操作;对应的解码操作是用 stringByRemovingPercentEncoding 方法
 33     fileURLStr = [fileURLStr stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
 34     NSURL *fileURL = [NSURL URLWithString:fileURLStr];
 35     
 36     //创建请求
 37     NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:fileURL];
 38     
 39     //创建会话(这里使用了一个全局会话)
 40     NSURLSession *session = [NSURLSession sharedSession];
 41     
 42     //创建下载任务,并且启动他;在非主线程中执行
 43     NSURLSessionDownloadTask *downloadTask = [session downloadTaskWithRequest:request completionHandler:^(NSURL *location, NSURLResponse *response, NSError *error) {
 44         __block void (^updateUI)(); //声明用于主线程更新 UI 的代码块
 45         
 46         if (!error) {
 47             NSLog(@"下载后的临时保存路径:%@", location);
 48             
 49             NSString *savePath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
 50             savePath = [savePath stringByAppendingPathComponent:_lblFileName.text];
 51             NSURL *saveURL = [NSURL fileURLWithPath:savePath];
 52             NSError *saveError;
 53             NSFileManager *fileManager = [NSFileManager defaultManager];
 54             //判断是否存在旧的目标文件,如果存在就先移除;避免无法复制问题
 55             if ([fileManager fileExistsAtPath:savePath]) {
 56                 [fileManager removeItemAtPath:savePath error:&saveError];
 57                 if (saveError) {
 58                     NSLog(@"移除旧的目标文件失败,错误信息:%@", saveError.localizedDescription);
 59                     
 60                     updateUI = ^ {
 61                         _lblMessage.text = @"下载失败";
 62                     };
 63                 }
 64             }
 65             if (!saveError) {
 66                 //把源文件复制到目标文件,当目标文件存在时,会抛出一个错误到 error 参数指向的对象实例
 67                 //方法一(path 不能有 file:// 前缀)
 68                 //                [fileManager copyItemAtPath:[location path]
 69                 //                                     toPath:savePath
 70                 //                                      error:&saveError];
 71                 
 72                 //方法二
 73                 [fileManager copyItemAtURL:location
 74                                      toURL:saveURL
 75                                      error:&saveError];
 76                 
 77                 if (!saveError) {
 78                     NSLog(@"保存成功");
 79                     
 80                     updateUI = ^ {
 81                         _lblMessage.text = @"下载完成";
 82                     };
 83                 } else {
 84                     NSLog(@"保存失败,错误信息:%@", saveError.localizedDescription);
 85                     
 86                     updateUI = ^ {
 87                         _lblMessage.text = @"下载失败";
 88                     };
 89                 }
 90             }
 91             
 92         } else {
 93             NSLog(@"下载失败,错误信息:%@", error.localizedDescription);
 94             
 95             updateUI = ^ {
 96                 _lblMessage.text = @"下载失败";
 97             };
 98         }
 99         
100         dispatch_async(dispatch_get_main_queue(), updateUI); //使用主队列异步方式(主线程)执行更新 UI 的代码块
101     }];
102     [downloadTask resume]; //恢复线程,启动任务
103 }
104 
105 @end 

NSURLSessionViewController.xib

 View Code 

NSURLSessionDelegateViewController.h

 1 #import <UIKit/UIKit.h>
 2 
 3 @interface NSURLSessionDelegateViewController : UIViewController <NSURLSessionDownloadDelegate>
 4 @property (strong, nonatomic) NSURLSessionDownloadTask *downloadTask;
 5 
 6 @property (strong, nonatomic) IBOutlet UILabel *lblFileName;
 7 @property (strong, nonatomic) IBOutlet UIProgressView *progVDownloadFile;
 8 @property (strong, nonatomic) IBOutlet UILabel *lblMessage;
 9 @property (strong, nonatomic) IBOutlet UIButton *btnDownloadFile;
10 @property (strong, nonatomic) IBOutlet UIButton *btnCancel;
11 @property (strong, nonatomic) IBOutlet UIButton *btnSuspend;
12 @property (strong, nonatomic) IBOutlet UIButton *btnResume;
13 
14 @end 

NSURLSessionDelegateViewController.m

  1 #import "NSURLSessionDelegateViewController.h"
  2 #import "UIButton+BeautifulButton.h"
  3 
  4 @interface NSURLSessionDelegateViewController ()
  5 - (void)layoutUI;
  6 - (NSURLSession *)defaultSession;
  7 - (NSURLSession *)backgroundSession;
  8 - (void)updateProgress:(int64_t)receiveDataLength totalDataLength:(int64_t)totalDataLength;
  9 @end
 10 
 11 @implementation NSURLSessionDelegateViewController
 12 
 13 - (void)viewDidLoad {
 14     [super viewDidLoad];
 15     
 16     [self layoutUI];
 17 }
 18 
 19 - (void)didReceiveMemoryWarning {
 20     [super didReceiveMemoryWarning];
 21     // Dispose of any resources that can be recreated.
 22 }
 23 
 24 - (void)layoutUI {
 25     self.navigationItem.title = kTitleOfNSURLSessionDelegate;
 26     self.view.backgroundColor = [UIColor colorWithWhite:0.95 alpha:1.000];
 27     
 28     [_btnDownloadFile beautifulButton:nil];
 29     [_btnCancel beautifulButton:[UIColor redColor]];
 30     [_btnSuspend beautifulButton:[UIColor purpleColor]];
 31     [_btnResume beautifulButton:[UIColor orangeColor]];
 32 }
 33 
 34 - (NSURLSession *)defaultSession {
 35     /*
 36      NSURLSession 支持进程三种会话:
 37      1、defaultSessionConfiguration:进程内会话(默认会话),用硬盘来缓存数据。
 38      2、ephemeralSessionConfiguration:临时的进程内会话(内存),不会将 cookie、缓存储存到本地,只会放到内存中,当应用程序退出后数据也会消失。
 39      3、backgroundSessionConfiguration:后台会话,相比默认会话,该会话会在后台开启一个线程进行网络数据处理。
 40      */
 41 
 42     //创建会话配置「进程内会话」
 43     NSURLSessionConfiguration *sessionConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration];
 44     sessionConfiguration.timeoutIntervalForRequest = 60.0; //请求超时时间;默认为60秒
 45     sessionConfiguration.allowsCellularAccess = YES; //是否允许蜂窝网络访问(2G/3G/4G)
 46     sessionConfiguration.HTTPMaximumConnectionsPerHost = 4; //限制每次最多连接数;在 iOS 中默认值为4
 47     
 48     //创建会话
 49     NSURLSession *session = [NSURLSession sessionWithConfiguration:sessionConfiguration
 50                                                           delegate:self
 51                                                      delegateQueue:nil];
 52     return session;
 53 }
 54 
 55 - (NSURLSession *)backgroundSession {
 56     static NSURLSession *session;
 57     static dispatch_once_t onceToken;
 58     dispatch_once(&onceToken, ^{ //应用程序生命周期内,只执行一次;保证只有一个「后台会话」
 59         //创建会话配置「后台会话」
 60         NSURLSessionConfiguration *sessionConfiguration = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:@"KMDownloadFile.NSURLSessionDelegateViewController"];
 61         sessionConfiguration.timeoutIntervalForRequest = 60.0; //请求超时时间;默认为60秒
 62         sessionConfiguration.allowsCellularAccess = YES; //是否允许蜂窝网络访问(2G/3G/4G)
 63         sessionConfiguration.HTTPMaximumConnectionsPerHost = 4; //限制每次最多连接数;在 iOS 中默认值为4
 64         sessionConfiguration.discretionary = YES; //是否自动选择最佳网络访问,仅对「后台会话」有效
 65         
 66         //创建会话
 67         session = [NSURLSession sessionWithConfiguration:sessionConfiguration
 68                                                 delegate:self
 69                                            delegateQueue:nil];
 70     });
 71     return session;
 72 }
 73 
 74 - (void)updateProgress:(int64_t)receiveDataLength totalDataLength:(int64_t)totalDataLength; {
 75     dispatch_async(dispatch_get_main_queue(), ^{ //使用主队列异步方式(主线程)执行更新 UI 操作
 76         if (receiveDataLength == totalDataLength) {
 77             _lblMessage.text = @"下载完成";
 78             kApplication.networkActivityIndicatorVisible = NO;
 79         } else {
 80             _lblMessage.text = @"下载中...";
 81             kApplication.networkActivityIndicatorVisible = YES;
 82             _progVDownloadFile.progress = (float)receiveDataLength / totalDataLength;
 83         }
 84     });
 85 }
 86 
 87 - (IBAction)downloadFile:(id)sender {
 88     NSString *fileURLStr = kFileURLStr;
 89     //编码操作;对应的解码操作是用 stringByRemovingPercentEncoding 方法
 90     fileURLStr = [fileURLStr stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
 91     NSURL *fileURL = [NSURL URLWithString:fileURLStr];
 92     
 93     //创建请求
 94     NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:fileURL];
 95     
 96     //创建会话「进程内会话」;如要用「后台会话」就使用自定义的[self backgroundSession] 方法
 97     NSURLSession *session = [self defaultSession];
 98     
 99     //创建下载任务,并且启动他;在非主线程中执行
100     _downloadTask = [session downloadTaskWithRequest:request];
101     [_downloadTask resume];
102     
103     /*
104      会话任务状态
105      typedef NS_ENUM(NSInteger, NSURLSessionTaskState) {
106      NSURLSessionTaskStateRunning = 0, //正在执行
107      NSURLSessionTaskStateSuspended = 1, //已挂起
108      NSURLSessionTaskStateCanceling = 2, //正在取消
109      NSURLSessionTaskStateCompleted = 3, //已完成
110      } NS_ENUM_AVAILABLE(NSURLSESSION_AVAILABLE, 7_0);
111      */
112 }
113 
114 - (IBAction)cancel:(id)sender {
115     [_downloadTask cancel];
116 }
117 
118 - (IBAction)suspend:(id)sender {
119     [_downloadTask suspend];
120     kApplication.networkActivityIndicatorVisible = NO;
121 }
122 
123 - (IBAction)resume:(id)sender {
124     [_downloadTask resume];
125 }
126 
127 #pragma mark - NSURLSessionDownloadDelegate
128 - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite {
129     NSLog(@"已经接收到响应数据,数据长度为%lld字节...", totalBytesWritten);
130     
131     [self updateProgress:totalBytesWritten totalDataLength:totalBytesExpectedToWrite];
132 }
133 
134 - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location {
135     //下载文件会临时保存,正常流程下系统最终会自动清除此临时文件;保存路径目录根据会话类型而有所不同:
136     //「进程内会话(默认会话)」和「临时的进程内会话(内存)」,路径目录为:/tmp,可以通过 NSTemporaryDirectory() 方法获取
137     //「后台会话」,路径目录为:/Library/Caches/com.apple.nsurlsessiond/Downloads/com.kenmu.KMDownloadFile
138     NSLog(@"已经接收完所有响应数据,下载后的临时保存路径:%@", location);
139     
140     __block void (^updateUI)(); //声明用于主线程更新 UI 的代码块
141     
142     NSString *savePath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
143     savePath = [savePath stringByAppendingPathComponent:_lblFileName.text];
144     NSURL *saveURL = [NSURL fileURLWithPath:savePath];
145     NSError *saveError;
146     NSFileManager *fileManager = [NSFileManager defaultManager];
147     //判断是否存在旧的目标文件,如果存在就先移除;避免无法复制问题
148     if ([fileManager fileExistsAtPath:savePath]) {
149         [fileManager removeItemAtPath:savePath error:&saveError];
150         if (saveError) {
151             NSLog(@"移除旧的目标文件失败,错误信息:%@", saveError.localizedDescription);
152             
153             updateUI = ^ {
154                 _lblMessage.text = @"下载失败";
155             };
156         }
157     }
158     if (!saveError) {
159         //把源文件复制到目标文件,当目标文件存在时,会抛出一个错误到 error 参数指向的对象实例
160         //方法一(path 不能有 file:// 前缀)
161         //                [fileManager copyItemAtPath:[location path]
162         //                                     toPath:savePath
163         //                                      error:&saveError];
164         
165         //方法二
166         [fileManager copyItemAtURL:location
167                              toURL:saveURL
168                              error:&saveError];
169         
170         if (!saveError) {
171             NSLog(@"保存成功");
172             
173             updateUI = ^ {
174                 _lblMessage.text = @"下载完成";
175             };
176         } else {
177             NSLog(@"保存失败,错误信息:%@", saveError.localizedDescription);
178             
179             updateUI = ^ {
180                 _lblMessage.text = @"下载失败";
181             };
182         }
183     }
184     
185     dispatch_async(dispatch_get_main_queue(), updateUI); //使用主队列异步方式(主线程)执行更新 UI 的代码块
186 }
187 
188 - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error {
189     NSLog(@"无论下载成功还是失败,最终都会执行一次");
190     
191     if (error) {
192         NSString *desc = error.localizedDescription;
193         NSLog(@"下载失败,错误信息:%@", desc);
194         
195         dispatch_async(dispatch_get_main_queue(), ^{ //使用主队列异步方式(主线程)执行更新 UI 操作
196             _lblMessage.text = [desc isEqualToString:@"cancelled"] ? @"下载已取消" : @"下载失败";
197             kApplication.networkActivityIndicatorVisible = NO;
198             _progVDownloadFile.progress = 0.0;
199         });
200     }
201 }
202 
203 @end

NSURLSessionDelegateViewController.xib

 View Code 

AFNetworkingViewController.h

 1 #import <UIKit/UIKit.h>
 2 #import "MBProgressHUD.h"
 3 
 4 @interface AFNetworkingViewController : UIViewController
 5 @property (strong, nonatomic) MBProgressHUD *hud;
 6 
 7 @property (strong, nonatomic) IBOutlet UILabel *lblFileName;
 8 @property (strong, nonatomic) IBOutlet UILabel *lblMessage;
 9 @property (strong, nonatomic) IBOutlet UIButton *btnDownloadFileByConnection;
10 @property (strong, nonatomic) IBOutlet UIButton *btnDownloadFileBySession;
11 
12 @end 

AFNetworkingViewController.m

  1 #import "AFNetworkingViewController.h"
  2 #import "AFNetworking.h"
  3 #import "AFNetworkActivityIndicatorManager.h"
  4 #import "UIButton+BeautifulButton.h"
  5 
  6 @interface AFNetworkingViewController ()
  7 - (void)showAlert:(NSString *)msg;
  8 - (void)checkNetwork;
  9 - (void)layoutUI;
 10 - (NSMutableURLRequest *)downloadRequest;
 11 - (NSURL *)saveURL:(NSURLResponse *)response deleteExistFile:(BOOL)deleteExistFile;
 12 - (void)updateProgress:(int64_t)receiveDataLength totalDataLength:(int64_t)totalDataLength;
 13 @end
 14 
 15 @implementation AFNetworkingViewController
 16 
 17 - (void)viewDidLoad {
 18     [super viewDidLoad];
 19     
 20     [self layoutUI];
 21 }
 22 
 23 - (void)didReceiveMemoryWarning {
 24     [super didReceiveMemoryWarning];
 25     // Dispose of any resources that can be recreated.
 26 }
 27 
 28 - (void)showAlert:(NSString *)msg {
 29     UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"网络情况"
 30                                                     message:msg
 31                                                    delegate:self
 32                                           cancelButtonTitle:nil
 33                                           otherButtonTitles:@"确定", nil];
 34     [alert show];
 35 }
 36 
 37 - (void)checkNetwork {
 38     NSURL *baseURL = [NSURL URLWithString:@"http://www.baidu.com/"];
 39     AFHTTPRequestOperationManager *manager = [[AFHTTPRequestOperationManager alloc] initWithBaseURL:baseURL];
 40     
 41     NSOperationQueue *operationQueue = manager.operationQueue;
 42     [manager.reachabilityManager setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) {
 43         switch (status) {
 44             case AFNetworkReachabilityStatusReachableViaWiFi:
 45                 [self showAlert:@"Wi-Fi 网络下"];
 46                 [operationQueue setSuspended:NO];
 47                 break;
 48             case AFNetworkReachabilityStatusReachableViaWWAN:
 49                 [self showAlert:@"2G/3G/4G 蜂窝移动网络下"];
 50                 [operationQueue setSuspended:YES];
 51                 break;
 52             case AFNetworkReachabilityStatusNotReachable:
 53             default:
 54                 [self showAlert:@"未连接网络"];
 55                 [operationQueue setSuspended:YES];
 56                 break;
 57         }
 58     }];
 59     
 60     [manager.reachabilityManager startMonitoring];
 61 }
 62 
 63 - (void)layoutUI {
 64     self.navigationItem.title = kTitleOfAFNetworking;
 65     self.view.backgroundColor = [UIColor colorWithWhite:0.95 alpha:1.000];
 66     
 67     //条件表达式中,「?:」可以表示在条件不成立的情况,才使用后者赋值,否则使用用于条件判断的前者赋值
 68     //以下语句等同于:UIButton *btn = _btnDownloadFileByConnection ? _btnDownloadFileByConnection : [UIButton new];
 69     //在 .NET 中,相当于使用「??」;在 JavaScript 中,相当于使用「||」来实现这种类似的判断
 70     UIButton *btn = _btnDownloadFileByConnection ?: [UIButton new];
 71     [btn beautifulButton:nil];
 72     [_btnDownloadFileBySession beautifulButton:[UIColor orangeColor]];
 73     
 74     //进度效果
 75     _hud = [[MBProgressHUD alloc] initWithView:self.view];
 76     _hud.mode = MBProgressHUDModeDeterminate;
 77     _hud.labelText = @"下载中...";
 78     [_hud hide:YES];
 79     [self.view addSubview:_hud];
 80     
 81     //检查网络情况
 82     [self checkNetwork];
 83     
 84     //启动网络活动指示器;会根据网络交互情况,实时显示或隐藏网络活动指示器;他通过「通知与消息机制」来实现 [UIApplication sharedApplication].networkActivityIndicatorVisible 的控制
 85     [AFNetworkActivityIndicatorManager sharedManager].enabled = YES;
 86 }
 87 
 88 - (NSMutableURLRequest *)downloadRequest {
 89     NSString *fileURLStr = kFileURLStr;
 90     //编码操作;对应的解码操作是用 stringByRemovingPercentEncoding 方法
 91     fileURLStr = [fileURLStr stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
 92     NSURL *fileURL = [NSURL URLWithString:fileURLStr];
 93     
 94     //创建请求
 95     NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:fileURL];
 96     return request;
 97 }
 98 
 99 - (NSURL *)saveURL:(NSURLResponse *)response deleteExistFile:(BOOL)deleteExistFile {
100     NSString *fileName = response ? [response suggestedFilename] : _lblFileName.text;
101     
102     //方法一
103 //    NSString *savePath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
104 //    savePath = [savePath stringByAppendingPathComponent:fileName];
105 //    NSURL *saveURL = [NSURL fileURLWithPath:savePath];
106     
107     //方法二
108     NSURL *saveURL = [[NSFileManager defaultManager] URLForDirectory:NSCachesDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:NO error:nil];
109     saveURL = [saveURL URLByAppendingPathComponent:fileName];
110     NSString *savePath = [saveURL path];
111     
112     if (deleteExistFile) {
113         NSError *saveError;
114         NSFileManager *fileManager = [NSFileManager defaultManager];
115         //判断是否存在旧的目标文件,如果存在就先移除;避免无法复制问题
116         if ([fileManager fileExistsAtPath:savePath]) {
117             [fileManager removeItemAtPath:savePath error:&saveError];
118             if (saveError) {
119                 NSLog(@"移除旧的目标文件失败,错误信息:%@", saveError.localizedDescription);
120             }
121         }
122     }
123     
124     return saveURL;
125 }
126 
127 - (void)updateProgress:(int64_t)receiveDataLength totalDataLength:(int64_t)totalDataLength; {
128     dispatch_async(dispatch_get_main_queue(), ^{ //使用主队列异步方式(主线程)执行更新 UI 操作
129         _hud.progress = (float)receiveDataLength / totalDataLength;
130         
131         if (receiveDataLength == totalDataLength) {
132             _lblMessage.text =  receiveDataLength < 0 ? @"下载失败" : @"下载完成";
133             //kApplication.networkActivityIndicatorVisible = NO;
134             [_hud hide:YES];
135         } else {
136             _lblMessage.text = @"下载中...";
137             //kApplication.networkActivityIndicatorVisible = YES;
138             [_hud show:YES];
139         }
140     });
141 }
142 
143 - (IBAction)downloadFileByConnection:(id)sender {
144     //创建请求
145     NSMutableURLRequest *request = [self downloadRequest];
146     
147     //创建请求操作
148     AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
149     NSString *savePath = [[self saveURL:nil deleteExistFile:NO] path];
150     
151     [operation setDownloadProgressBlock:^(NSUInteger bytesRead, long long totalBytesRead, long long totalBytesExpectedToRead) {
152         NSLog(@"已经接收到响应数据,数据长度为%lld字节...", totalBytesRead);
153         
154         [self updateProgress:totalBytesRead totalDataLength:totalBytesExpectedToRead];
155     }];
156     
157     [operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
158         NSLog(@"已经接收完所有响应数据");
159         
160         NSData *data = (NSData *)responseObject;
161         [data writeToFile:savePath atomically:YES]; //responseObject 的对象类型是 NSData
162         
163         [self updateProgress:100 totalDataLength:100];
164     } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
165         NSLog(@"下载失败,错误信息:%@", error.localizedDescription);
166         
167         [self updateProgress:-1 totalDataLength:-1];
168     }];
169     
170     //启动请求操作
171     [operation start];
172 }
173 
174 - (IBAction)downloadFileBySession:(id)sender {
175     //创建请求
176     NSMutableURLRequest *request = [self downloadRequest];
177     
178     //创建会话配置「进程内会话」
179     NSURLSessionConfiguration *sessionConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration];
180     sessionConfiguration.timeoutIntervalForRequest = 60.0; //请求超时时间;默认为60秒
181     sessionConfiguration.allowsCellularAccess = YES; //是否允许蜂窝网络访问(2G/3G/4G)
182     sessionConfiguration.HTTPMaximumConnectionsPerHost = 4; //限制每次最多连接数;在 iOS 中默认值为4
183     
184     //创建会话管理器
185     AFURLSessionManager *sessionManager = [[AFURLSessionManager alloc] initWithSessionConfiguration:sessionConfiguration];
186     
187     //创建会话下载任务,并且启动他;在非主线程中执行
188     NSURLSessionDownloadTask *task = [sessionManager
189                                       downloadTaskWithRequest:request
190                                       progress:nil
191                                       destination:^ NSURL*(NSURL *targetPath, NSURLResponse *response) {
192                                           //当 sessionManager 调用 setDownloadTaskDidFinishDownloadingBlock: 方法,并且方法代码块返回值不为 nil 时(优先级高),下面的两句代码是不执行的(优先级低)
193                                           NSLog(@"下载后的临时保存路径:%@", targetPath);
194                                           return [self saveURL:response deleteExistFile:YES];
195                                       } completionHandler:^ (NSURLResponse *response, NSURL *filePath, NSError *error) {
196                                           if (!error) {
197                                               NSLog(@"下载后的保存路径:%@", filePath); //为上面代码块返回的路径
198                                               
199                                               [self updateProgress:100 totalDataLength:100];
200                                           } else {
201                                               NSLog(@"下载失败,错误信息:%@", error.localizedDescription);
202                                               
203                                               [self updateProgress:-1 totalDataLength:-1];
204                                           }
205                                           
206                                           [_hud hide:YES];
207                                       }];
208     
209     //类似 NSURLSessionDownloadDelegate 的方法操作
210     //- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite;
211     [sessionManager setDownloadTaskDidWriteDataBlock:^ (NSURLSession *session, NSURLSessionDownloadTask *downloadTask, int64_t bytesWritten, int64_t totalBytesWritten, int64_t totalBytesExpectedToWrite) {
212         NSLog(@"已经接收到响应数据,数据长度为%lld字节...", totalBytesWritten);
213         
214         [self updateProgress:totalBytesWritten totalDataLength:totalBytesExpectedToWrite];
215     }];
216     
217     //- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location;
218     [sessionManager setDownloadTaskDidFinishDownloadingBlock:^ NSURL*(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, NSURL *location) {
219         NSLog(@"已经接收完所有响应数据,下载后的临时保存路径:%@", location);
220         return [self saveURL:nil deleteExistFile:YES];
221     }];
222     
223     [task resume];
224 }
225 
226 @end

AFNetworkingViewController.xib

 View Cod 

AppDelegate.h

1 #import <UIKit/UIKit.h>
2 
3 @interface AppDelegate : UIResponder <UIApplicationDelegate>
4 
5 @property (strong, nonatomic) UIWindow *window;
6 @property (strong, nonatomic) UINavigationController *navigationController;
7 
8 @end 

AppDelegate.m

 1 #import "AppDelegate.h"
 2 #import "ViewController.h"
 3 
 4 @interface AppDelegate ()
 5 
 6 @end
 7 
 8 @implementation AppDelegate
 9 
10 
11 - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
12     _window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
13     ViewController *viewController = [[ViewController alloc]
14                                       initWithSampleNameArray:@[ kTitleOfNSURLConnection,
15                                                                  kTitleOfNSURLConnectionDelegate,
16                                                                  kTitleOfNSURLSession,
17                                                                  kTitleOfNSURLSessionDelegate,
18                                                                  kTitleOfAFNetworking]];
19     _navigationController = [[UINavigationController alloc] initWithRootViewController:viewController];
20     _window.rootViewController = _navigationController;
21     //[_window addSubview:_navigationController.view]; //当_window.rootViewController关联时,这一句可有可无
22     [_window makeKeyAndVisible];
23     return YES;
24 }
25 
26 - (void)applicationWillResignActive:(UIApplication *)application {
27 }
28 
29 - (void)applicationDidEnterBackground:(UIApplication *)application {
30 }
31 
32 - (void)applicationWillEnterForeground:(UIApplication *)application {
33 }
34 
35 - (void)applicationDidBecomeActive:(UIApplication *)application {
36 }
37 
38 - (void)applicationWillTerminate:(UIApplication *)application {
39 }
40 
41 @end
原文地址:https://www.cnblogs.com/fshmjl/p/4852268.html