iOS开发基础44-网络编程之NSURLSession&AFN

在iOS开发中,网络请求是一个非常常见的需求。本文将详细介绍三种网络请求的方式:NSURLConnectionNSURLSession、以及第三方库AFNetworking,并对相关知识点进行补充和分析。

一、NSURLConnection(补充)

NSURLConnection是一种较早期的iOS网络请求工具,但它仍然有很大一部分应用在使用。它可以通过同步和异步方式来进行网络请求。

NSURLConnection的使用

1. 初始化NSURLConnection请求

使用NSURLConnection进行异步请求需要实现相关的Delegate方法:

@interface MyViewController () <NSURLConnectionDataDelegate>
@property (nonatomic, strong) NSMutableData *receivedData;
@end

@implementation MyViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    NSURL *url = [NSURL URLWithString:@"http://example.com"];
    NSURLRequest *request = [NSURLRequest requestWithURL:url];
    NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
    [connection start];
}

2. NSURLConnectionDataDelegate方法

在实现NSURLConnectionDataDelegate协议时,需要确保各种状态处理的细节。

- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
    self.receivedData = [[NSMutableData alloc] init];
}

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
    [self.receivedData appendData:data];
}

- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
    // Handle received data
}

- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
    NSLog(@"Connection failed: %@", error.localizedDescription);
}

3. 线程处理

默认情况下,NSURLConnection的回调方法是在主线程中执行的。如果在子线程中发送请求,需要手动将NSURLConnection加入到RunLoop

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"http://example.com"]];
    NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:NO];
    [connection scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
    [connection start];
    [[NSRunLoop currentRunLoop] run];
});

二、NSURLSession

NSURLSessionNSURLConnection的升级版,提供了更加现代、灵活的API,支持后台下载、任务恢复、数据保护等功能。

NSURLSession的使用

1. 创建NSURLSession

有多种方式创建NSURLSession,可以通过共享session、默认session或自定义session:

NSURLSession *session = [NSURLSession sharedSession];

或者创建带有配置的自定义session:

NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSession *session = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:nil];

2. 创建任务并执行

可以创建数据任务、下载任务或上传任务:

NSURL *url = [NSURL URLWithString:@"http://example.com"];
NSURLSessionDataTask *dataTask = [session dataTaskWithURL:url completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
    if (error) {
        NSLog(@"Error: %@", error);
    } else {
        // Handle data
    }
}];
[dataTask resume];

NSURLSessionTask常用方法

[task suspend]; // 暂停任务
[task resume];  // 继续任务
[task cancel];  // 取消任务

取消任务时可以保存进度:

[task cancelByProducingResumeData:^(NSData *resumeData) {
    // Save resumeData
}];

代理方法

NSURLSessionDataDelegate

- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler {
    completionHandler(NSURLSessionResponseAllow); // 允许接收数据
}

- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data {
    // Handle received data
}

- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error {
    if (error) {
        NSLog(@"Error: %@", error);
    } else {
        // Task completed successfully
    }
}

NSURLSessionDownloadDelegate

- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite {
    // Handle download progress
}

- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location {
    // Handle file download completion
}

NSURLSessionTaskDelegate

- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didSendBodyData:(int64_t)bytesSent totalBytesSent:(int64_t)totalBytesSent totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend {
    // Handle upload progress
}

断点下载

断点下载在需要暂停和恢复任务时非常有用。以下是一个简单的断点下载实现:

#import "ViewController.h"

@interface ViewController () <NSURLSessionDataDelegate>

@property (nonatomic, strong) NSURLSession *session;
@property (nonatomic, strong) NSURLSessionDataTask *dataTask;
@property (nonatomic, strong) NSOutputStream *outputStream;
@property (nonatomic, assign) NSUInteger totalLength;
@property (nonatomic, assign) NSUInteger currentLength;
@property (nonatomic, strong) NSData *resumeData;
@property (weak, nonatomic) IBOutlet UIProgressView *progressView;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    self.currentLength = [self getFileSizeAtPath:[self filePath]];
}

- (IBAction)startDownload:(id)sender {
    [self.dataTask resume];
}

- (IBAction)pauseDownload:(id)sender {
    [self.dataTask suspend];
}

- (IBAction)resumeDownload:(id)sender {
    if (self.resumeData) {
        self.dataTask = [self.session downloadTaskWithResumeData:self.resumeData];
    }
    [self.dataTask resume];
}

- (NSUInteger)getFileSizeAtPath:(NSString *)path {
    if ([[NSFileManager defaultManager] fileExistsAtPath:path]) {
        NSDictionary *attributes = [[NSFileManager defaultManager] attributesOfItemAtPath:path error:nil];
        return [attributes[NSFileSize] unsignedIntegerValue];
    }
    return 0;
}

- (NSString *)filePath {
    return [NSTemporaryDirectory() stringByAppendingPathComponent:@"downloadedFile"];
}

#pragma mark - NSURLSessionDataDelegate

- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler {
    self.totalLength = response.expectedContentLength + self.currentLength;
    self.outputStream = [NSOutputStream outputStreamToFileAtPath:[self filePath] append:YES];
    [self.outputStream open];
    completionHandler(NSURLSessionResponseAllow);
}

- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data {
    [self.outputStream write:data.bytes maxLength:data.length];
    self.currentLength += data.length;
    self.progressView.progress = 1.0 * self.currentLength / self.totalLength;
}

- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error {
    if (!error) {
        NSLog(@"Download completed.");
    } else {
        NSLog(@"Download failed: %@", error.localizedDescription);
    }
    [self.outputStream close];
}

#pragma mark - Lazy Loading

- (NSURLSession *)session {
    if (!_session) {
        NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
        _session = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:[NSOperationQueue mainQueue]];
    }
    return _session;
}

- (NSURLSessionDataTask *)dataTask {
    if (!_dataTask) {
        NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"http://example.com/path/to/file"]];
        NSString *range = [NSString stringWithFormat:@"bytes=%zd-", self.currentLength];
        [request setValue:range forHTTPHeaderField:@"Range"];
        _dataTask = [self.session dataTaskWithRequest:request];
    }
    return _dataTask;
}

@end

三、AFNetworking

AFNetworking是一个基于NSURLSession的流行iOS网络请求库,提供了更简洁高效的API,用于处理网络请求、文件下载、上传和序列化等操作。

AFNetworking的使用

1. 安装AFNetworking

推荐使用CocoaPods来安装AFNetworking

pod 'AFNetworking', '~> 4.0'

然后运行pod install

2. 使用AFHTTPRequestOperationManager进行请求

GET请求
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
[manager GET:@"http://example.com/api" parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
    NSLog(@"JSON: %@", responseObject);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
    NSLog(@"Error: %@", error);
}];
POST请求
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
NSDictionary *parameters = @{@"foo": @"bar"};
[manager POST:@"http://example.com/api" parameters:parameters success:^(AFHTTPRequestOperation *operation, id responseObject) {
    NSLog(@"JSON: %@", responseObject);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
    NSLog(@"Error: %@", error);
}];

3. 使用AFHTTPSessionManager进行请求

GET请求
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
[manager GET:@"http://example.com/api" parameters:nil headers:nil progress:nil success:^(NSURLSessionDataTask *task, id responseObject) {
    NSLog(@"JSON: %@", responseObject);
} failure:^(NSURLSessionDataTask *task, NSError *error) {
    NSLog(@"Error: %@", error);
}];
POST请求
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
NSDictionary *parameters = @{@"foo": @"bar"};
[manager POST:@"http://example.com/api" parameters:parameters headers:nil progress:nil success:^(NSURLSessionDataTask *task, id responseObject) {
    NSLog(@"JSON: %@", responseObject);
} failure:^(NSURLSessionDataTask *task, NSError *error) {
    NSLog(@"Error: %@", error);
}];

4. 文件下载

AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"http://example.com/file.zip"]];
NSURLSessionDownloadTask *downloadTask = [manager downloadTaskWithRequest:request progress:nil destination:^NSURL *(NSURL *targetPath, NSURLResponse *response) {
    NSURL *documentsDirectoryURL = [[NSFileManager defaultManager] URLForDirectory:NSDocumentDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:NO error:nil];
    return [documentsDirectoryURL URLByAppendingPathComponent:[response suggestedFilename]];
} completionHandler:^(NSURLResponse *response, NSURL *filePath, NSError *error) {
    if (!error) {
        NSLog(@"File downloaded to: %@", filePath);
    } else {
        NSLog(@"Error: %@", error);
    }
}];
[downloadTask resume];

5. 文件上传

AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
NSDictionary *parameters = @{@"foo": @"bar"};
[manager POST:@"http://example.com/upload" parameters:parameters headers:nil constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {
    NSURL *fileURL = [NSURL fileURLWithPath:@"filePath"];
    [formData appendPartWithFileURL:fileURL name:@"file" error:nil];
} progress:nil success:^(NSURLSessionDataTask *task, id responseObject) {
    NSLog(@"Success: %@", responseObject);
} failure:^(NSURLSessionDataTask *task, NSError *error) {
    NSLog(@"Error: %@", error);
}];

6. 序列化

AFNetworking默认支持JSON数据的自动序列化和反序列化,但也提供了灵活的自定义方法:

AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
manager.responseSerializer = [AFXMLParserResponseSerializer serializer];
// or
manager.responseSerializer = [AFHTTPResponseSerializer serializer];

7. 网络状态检测

AFNetworkReachabilityManager *manager = [AFNetworkReachabilityManager sharedManager];
[manager setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) {
    switch (status) {
        case AFNetworkReachabilityStatusReachableViaWWAN:
            NSLog(@"Cellular");
            break;
        case AFNetworkReachabilityStatusReachableViaWiFi:
            NSLog(@"WiFi");
            break;
        case AFNetworkReachabilityStatusNotReachable:
            NSLog(@"No Connection");
            break;
        default:
            break;
    }
}];
[manager startMonitoring];

六、总结

在iOS网络请求中,NSURLConnectionNSURLSessionAFNetworking各有特点:

  • NSURLConnection是最基础的方式,但需要手动管理较多的细节。
  • NSURLSession提供了更多现代化特性和更好的性能。
  • AFNetworking基于NSURLSession,提供了更简洁的API,简化了常见操作。

选择哪种方式取决于具体的项目需求和开发者的习惯。

posted @ 2015-08-24 19:18  Mr.陳  阅读(1215)  评论(0编辑  收藏  举报