MK
摩柯社区 - 一个极简的技术知识社区
AI 面试

Objective-C网络编程与NSURLSession应用

2022-05-023.4k 阅读

一、NSURLSession 简介

在Objective-C的网络编程中,NSURLSession是苹果公司在iOS 7.0和OS X 10.9引入的强大网络框架,用于替代旧的NSURLConnectionNSURLSession提供了一个简单且强大的方式来处理基于URL的网络请求,包括数据加载、文件下载和上传等操作。它支持多种任务类型,如数据任务、下载任务和上传任务,并且对连接的管理和复用有很好的优化,能有效提高网络请求的效率。

NSURLSession是基于任务(NSURLSessionTask)的模型。一个NSURLSession对象可以创建多个不同类型的任务,每个任务负责处理一个具体的网络请求。NSURLSession的设计使得代码结构更加清晰,便于管理和维护不同类型的网络操作。

二、NSURLSession 的创建与配置

2.1 创建NSURLSessionConfiguration对象

在使用NSURLSession之前,首先需要创建一个NSURLSessionConfiguration对象。这个对象用于配置NSURLSession的各种属性,例如请求超时时间、缓存策略、认证信息等。NSURLSessionConfiguration有几种不同的子类,以满足不同的需求:

  • NSURLSessionConfiguration.default:默认配置,适用于大多数常见的网络请求场景。它会使用系统默认的缓存策略、超时时间等设置。
  • NSURLSessionConfiguration.ephemeral:临时配置,这种配置下的NSURLSession不会将任何数据写入磁盘,包括缓存数据、Cookie等。适用于需要临时处理敏感数据的网络请求,请求结束后不会在设备上留下任何相关数据。
  • NSURLSessionConfiguration.background:后台配置,用于在应用进入后台时仍能继续执行网络任务。这种配置下的网络任务可以在应用被挂起甚至关闭时,由系统在后台调度执行。

以下是创建不同类型NSURLSessionConfiguration对象的示例代码:

// 创建默认配置
NSURLSessionConfiguration *defaultConfigObject = [NSURLSessionConfiguration defaultSessionConfiguration];
// 创建临时配置
NSURLSessionConfiguration *ephemeralConfigObject = [NSURLSessionConfiguration ephemeralSessionConfiguration];
// 创建后台配置
NSURLSessionConfiguration *backgroundConfigObject = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:@"com.example.backgroundSession"];

在上述代码中,backgroundSessionConfigurationWithIdentifier:方法需要传入一个唯一的标识符,用于标识这个后台会话。这个标识符在应用的不同启动周期中用于关联同一个后台会话。

2.2 创建NSURLSession对象

创建好NSURLSessionConfiguration对象后,就可以使用它来创建NSURLSession对象。NSURLSession有两种常见的创建方式:

  1. 使用共享会话NSURLSession提供了一个共享的单例对象[NSURLSession sharedSession],它使用默认的配置。共享会话适用于不需要特殊配置的简单网络请求场景,例如大多数应用内的常规数据获取。
NSURLSession *sharedSession = [NSURLSession sharedSession];
  1. 自定义配置的会话:通过传入NSURLSessionConfiguration对象来创建具有特定配置的NSURLSession对象。
NSURLSession *customSession = [NSURLSession sessionWithConfiguration:defaultConfigObject];

在创建自定义配置的会话时,除了传入NSURLSessionConfiguration对象,还可以传入一个委托对象(NSURLSessionDelegate或其相关子类的对象),用于处理网络请求过程中的各种事件,如认证挑战、数据接收等。如果不传入委托对象,则会话不会处理这些事件,可能导致请求失败。

三、NSURLSession 数据任务

3.1 基本数据任务

数据任务(NSURLSessionDataTask)用于从指定的URL加载数据,并将响应数据以NSData的形式返回。这是最常见的网络请求类型之一,适用于获取JSON、XML等格式的数据。

以下是一个简单的数据任务示例,用于获取一个URL的内容:

NSURL *url = [NSURL URLWithString:@"https://example.com/api/data"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
    if (error) {
        NSLog(@"请求失败: %@", error);
    } else {
        NSString *responseString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
        NSLog(@"响应数据: %@", responseString);
    }
}];
[dataTask resume];

在上述代码中:

  • 首先创建一个NSURL对象,指定请求的URL。
  • 然后通过NSURL创建一个NSURLRequest对象,NSURLRequest可以对请求进行更多的设置,如请求方法(GET、POST等)、请求头信息等。这里使用默认的GET请求。
  • 使用共享会话创建一个NSURLSessionDataTask对象,并传入NSURLRequest对象。completionHandler是一个块(block),在任务完成后会被调用,通过这个块可以获取到响应数据(data)、响应对象(response)以及可能发生的错误(error)。
  • 最后调用resume方法启动任务,如果不调用resume,任务不会开始执行。

3.2 带参数的POST请求数据任务

对于需要传递参数的POST请求,可以通过修改NSURLRequest的属性来实现。以下是一个发送POST请求并传递JSON格式参数的示例:

NSURL *url = [NSURL URLWithString:@"https://example.com/api/submit"];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
request.HTTPMethod = @"POST";
NSDictionary *parameters = @{@"key1": @"value1", @"key2": @"value2"};
NSError *error;
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:parameters options:0 error:&error];
if (error) {
    NSLog(@"JSON序列化错误: %@", error);
    return;
}
request.HTTPBody = jsonData;
request.additionalHeaders = @{@"Content-Type": @"application/json"};
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
    if (error) {
        NSLog(@"请求失败: %@", error);
    } else {
        NSString *responseString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
        NSLog(@"响应数据: %@", responseString);
    }
}];
[dataTask resume];

在这个示例中:

  • 创建一个NSMutableURLRequest对象,因为需要修改请求的属性,如请求方法和请求体。
  • 设置请求方法为POST
  • 将参数以字典的形式定义,然后使用NSJSONSerialization将字典转换为JSON格式的数据。
  • 将JSON数据设置为请求体,并添加Content-Type头信息,告诉服务器请求数据的格式是JSON。
  • 后续创建数据任务和处理响应的过程与GET请求类似。

四、NSURLSession 下载任务

4.1 简单下载任务

下载任务(NSURLSessionDownloadTask)用于从指定的URL下载文件。它会将文件下载到一个临时位置,任务完成后,需要将文件移动到最终的目标位置。

以下是一个简单的下载任务示例:

NSURL *url = [NSURL URLWithString:@"https://example.com/file.zip"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDownloadTask *downloadTask = [session downloadTaskWithRequest:request completionHandler:^(NSURL * _Nullable location, NSURLResponse * _Nullable response, NSError * _Nullable error) {
    if (error) {
        NSLog(@"下载失败: %@", error);
    } else {
        NSFileManager *fileManager = [NSFileManager defaultManager];
        NSURL *documentsDirectoryURL = [[fileManager URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] firstObject];
        NSURL *destinationURL = [documentsDirectoryURL URLByAppendingPathComponent:response.suggestedFilename];
        NSError *moveError;
        [fileManager moveItemAtURL:location toURL:destinationURL error:&moveError];
        if (moveError) {
            NSLog(@"移动文件失败: %@", moveError);
        } else {
            NSLog(@"文件下载并保存成功: %@", destinationURL);
        }
    }
}];
[downloadTask resume];

在上述代码中:

  • 创建NSURLNSURLRequest对象,指定下载的URL。
  • 使用共享会话创建NSURLSessionDownloadTask对象,completionHandler块在下载完成后被调用。
  • completionHandler中,首先获取文件管理器(NSFileManager),然后获取应用的文档目录URL。
  • 使用response.suggestedFilename获取服务器建议的文件名,并将其附加到文档目录URL后,得到目标文件的URL。
  • 最后使用fileManagermoveItemAtURL:toURL:error:方法将临时位置的下载文件移动到目标位置。

4.2 下载进度跟踪

在下载过程中,有时需要跟踪下载进度,以便向用户展示下载的实时状态。可以通过实现NSURLSessionDownloadDelegate协议来获取下载进度信息。

首先,创建NSURLSession时需要传入一个遵守NSURLSessionDownloadDelegate协议的委托对象:

@interface DownloadManager : NSObject <NSURLSessionDownloadDelegate>
@end

@implementation DownloadManager
// 实现下载进度跟踪方法
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite {
    float progress = (float)totalBytesWritten / (float)totalBytesExpectedToWrite;
    NSLog(@"下载进度: %.2f%%", progress * 100);
}
@end

// 创建NSURLSession并传入委托对象
NSURL *url = [NSURL URLWithString:@"https://example.com/file.zip"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
DownloadManager *downloadManager = [[DownloadManager alloc] init];
NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:downloadManager delegateQueue:nil];
NSURLSessionDownloadTask *downloadTask = [session downloadTaskWithRequest:request];
[downloadTask resume];

在上述代码中:

  • 定义了一个DownloadManager类,实现了NSURLSessionDownloadDelegate协议的URLSession:downloadTask:didWriteData:totalBytesWritten:totalBytesExpectedToWrite:方法。
  • 在该方法中,通过计算已下载字节数与总字节数的比例,得到下载进度,并打印输出。
  • 创建NSURLSession时,传入DownloadManager对象作为委托,这样在下载过程中会调用委托方法来更新下载进度。

五、NSURLSession 上传任务

5.1 简单上传任务

上传任务(NSURLSessionUploadTask)用于将本地数据上传到服务器。可以上传NSData对象,例如图片数据、文本数据等。

以下是一个简单的上传任务示例,将本地的一个文本文件内容上传到服务器:

NSURL *url = [NSURL URLWithString:@"https://example.com/api/upload"];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
request.HTTPMethod = @"POST";
NSString *filePath = [[NSBundle mainBundle] pathForResource:@"example" ofType:@"txt"];
NSData *fileData = [NSData dataWithContentsOfFile:filePath];
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionUploadTask *uploadTask = [session uploadTaskWithRequest:request fromData:fileData completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
    if (error) {
        NSLog(@"上传失败: %@", error);
    } else {
        NSString *responseString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
        NSLog(@"响应数据: %@", responseString);
    }
}];
[uploadTask resume];

在上述代码中:

  • 创建NSURLNSMutableURLRequest对象,设置请求方法为POST
  • 获取本地文本文件的路径,并将文件内容读取为NSData对象。
  • 使用共享会话创建NSURLSessionUploadTask对象,传入请求和要上传的数据。
  • completionHandler块中处理上传完成后的响应,判断是否上传成功,并打印响应数据。

5.2 带进度跟踪的上传任务

与下载任务类似,上传任务也可以跟踪进度。通过实现NSURLSessionTaskDelegate协议的URLSession:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend:方法来获取上传进度。

首先,创建一个遵守NSURLSessionTaskDelegate协议的类:

@interface UploadManager : NSObject <NSURLSessionTaskDelegate>
@end

@implementation UploadManager
// 实现上传进度跟踪方法
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didSendBodyData:(int64_t)bytesSent totalBytesSent:(int64_t)totalBytesSent totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend {
    float progress = (float)totalBytesSent / (float)totalBytesExpectedToSend;
    NSLog(@"上传进度: %.2f%%", progress * 100);
}
@end

// 创建NSURLSession并传入委托对象
NSURL *url = [NSURL URLWithString:@"https://example.com/api/upload"];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
request.HTTPMethod = @"POST";
NSString *filePath = [[NSBundle mainBundle] pathForResource:@"example" ofType:@"txt"];
NSData *fileData = [NSData dataWithContentsOfFile:filePath];
UploadManager *uploadManager = [[UploadManager alloc] init];
NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:uploadManager delegateQueue:nil];
NSURLSessionUploadTask *uploadTask = [session uploadTaskWithRequest:request fromData:fileData];
[uploadTask resume];

在上述代码中:

  • 定义了UploadManager类,实现了NSURLSessionTaskDelegate协议的URLSession:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend:方法,用于计算和打印上传进度。
  • 创建NSURLSession时,传入UploadManager对象作为委托,在上传过程中,该委托方法会被调用以更新上传进度。

六、NSURLSession 委托与事件处理

6.1 认证挑战处理

在进行网络请求时,有时服务器会要求进行认证,例如HTTP基本认证。NSURLSession通过委托方法来处理认证挑战。需要实现NSURLSessionDelegate协议的URLSession:didReceiveChallenge:completionHandler:方法。

以下是一个简单的处理HTTP基本认证的示例:

@interface AuthenticationManager : NSObject <NSURLSessionDelegate>
@end

@implementation AuthenticationManager
- (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential * _Nullable))completionHandler {
    if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodHTTPBasic]) {
        NSString *username = @"yourUsername";
        NSString *password = @"yourPassword";
        NSURLCredential *credential = [NSURLCredential credentialWithUser:username password:password persistence:NSURLCredentialPersistenceForSession];
        completionHandler(NSURLSessionAuthChallengeUseCredential, credential);
    } else {
        completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, nil);
    }
}
@end

// 创建NSURLSession并传入委托对象
NSURL *url = [NSURL URLWithString:@"https://example.com/protected"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
AuthenticationManager *authManager = [[AuthenticationManager alloc] init];
NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:authManager delegateQueue:nil];
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request];
[dataTask resume];

在上述代码中:

  • 定义了AuthenticationManager类,实现了URLSession:didReceiveChallenge:completionHandler:方法。
  • 在方法中,首先判断认证方法是否为HTTP基本认证。如果是,则创建一个包含用户名和密码的NSURLCredential对象,并通过completionHandler传递认证凭证,告诉系统使用该凭证进行认证。如果不是HTTP基本认证,则取消认证挑战。
  • 创建NSURLSession时,传入AuthenticationManager对象作为委托,以便在遇到认证挑战时能够正确处理。

6.2 重定向处理

当服务器返回重定向响应(HTTP 301、302等状态码)时,NSURLSession可以通过委托方法来决定如何处理重定向。需要实现NSURLSessionTaskDelegate协议的URLSession:task:willPerformHTTPRedirection:newRequest:completionHandler:方法。

以下是一个简单的重定向处理示例,默认跟随重定向:

@interface RedirectionManager : NSObject <NSURLSessionTaskDelegate>
@end

@implementation RedirectionManager
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task willPerformHTTPRedirection:(NSHTTPURLResponse *)response newRequest:(NSURLRequest *)request completionHandler:(void (^)(NSURLRequest * _Nullable))completionHandler {
    completionHandler(request);
}
@end

// 创建NSURLSession并传入委托对象
NSURL *url = [NSURL URLWithString:@"https://example.com/redirect"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
RedirectionManager *redirectionManager = [[RedirectionManager alloc] init];
NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:redirectionManager delegateQueue:nil];
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request];
[dataTask resume];

在上述代码中:

  • 定义了RedirectionManager类,实现了URLSession:task:willPerformHTTPRedirection:newRequest:completionHandler:方法。
  • 在方法中,直接通过completionHandler返回新的请求,这样NSURLSession会自动跟随重定向。如果需要自定义重定向行为,可以对新请求进行修改后再返回。
  • 创建NSURLSession时,传入RedirectionManager对象作为委托,以处理重定向事件。

七、NSURLSession 与多线程

NSURLSession在设计上充分考虑了多线程的支持。默认情况下,NSURLSession的任务会在后台线程中执行,以避免阻塞主线程,保证应用的流畅性。

7.1 使用默认队列

当创建NSURLSession时,如果不传入delegateQueue参数,NSURLSession会使用一个默认的后台队列来执行任务和调用委托方法。例如:

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

在这种情况下,NSURLSession会在一个系统管理的后台队列中执行任务,当任务完成或发生特定事件(如接收到数据、认证挑战等)时,会在这个后台队列中调用委托方法。如果委托方法中需要更新UI,由于UI更新必须在主线程中进行,所以需要切换到主线程。例如:

- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data {
    dispatch_async(dispatch_get_main_queue(), ^{
        // 更新UI的代码
    });
}

7.2 自定义队列

可以通过传入一个自定义的NSOperationQueue作为delegateQueue参数,来指定NSURLSession使用的队列。这样可以更精确地控制任务的执行和委托方法的调用。例如:

NSOperationQueue *customQueue = [[NSOperationQueue alloc] init];
customQueue.maxConcurrentOperationCount = 1; // 设置为串行队列
NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:customQueue];

在上述代码中,创建了一个自定义的串行队列,并将其作为delegateQueue传入NSURLSession的创建方法。这样,NSURLSession的任务和委托方法会在这个自定义的串行队列中执行。如果需要并行执行任务,可以适当调整maxConcurrentOperationCount的值。

八、NSURLSession 的缓存策略

NSURLSession支持多种缓存策略,通过NSURLRequestcachePolicy属性来设置。常见的缓存策略有以下几种:

8.1 NSURLRequestUseProtocolCachePolicy

这是默认的缓存策略,它遵循协议的规定来处理缓存。对于HTTP请求,它会根据服务器返回的Cache-ControlExpires头信息来决定是否使用缓存以及缓存的有效期。如果服务器没有明确的缓存指示,它可能会根据请求方法(GET请求更倾向于使用缓存)和其他因素来决定。

8.2 NSURLRequestReloadIgnoringLocalCacheData

这种策略会忽略本地缓存数据,每次都从服务器重新加载数据。适用于需要获取最新数据的场景,例如实时数据的请求。示例代码如下:

NSURL *url = [NSURL URLWithString:@"https://example.com/api/liveData"];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
request.cachePolicy = NSURLRequestReloadIgnoringLocalCacheData;

8.3 NSURLRequestReturnCacheDataElseLoad

这种策略会先尝试从本地缓存中获取数据,如果缓存中没有数据,则从服务器加载。适用于对数据实时性要求不是特别高,且希望在网络不佳时仍能显示部分数据的场景。示例代码如下:

NSURL *url = [NSURL URLWithString:@"https://example.com/api/info"];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
request.cachePolicy = NSURLRequestReturnCacheDataElseLoad;

8.4 NSURLRequestReturnCacheDataDontLoad

这种策略会只从本地缓存中获取数据,如果缓存中没有数据,则不进行网络加载,直接返回缓存中已有的过期数据(如果有)。适用于完全依赖本地缓存数据,且不希望进行任何网络请求的场景,例如离线应用的部分功能。示例代码如下:

NSURL *url = [NSURL URLWithString:@"https://example.com/api/offlineInfo"];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
request.cachePolicy = NSURLRequestReturnCacheDataDontLoad;

通过合理设置缓存策略,可以在提高应用性能的同时,节省用户的流量,并提供更好的用户体验。

九、NSURLSession 的常见问题与解决方法

9.1 网络请求超时

在网络不稳定或服务器响应缓慢的情况下,可能会出现网络请求超时的问题。可以通过NSURLSessionConfigurationtimeoutIntervalForRequest属性来设置请求的超时时间。例如:

NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
config.timeoutIntervalForRequest = 15; // 设置超时时间为15秒
NSURLSession *session = [NSURLSession sessionWithConfiguration:config];

如果在设置的超时时间内没有收到服务器的响应,NSURLSession会调用completionHandler并传入相应的超时错误。在处理错误时,可以提示用户网络请求超时,并根据应用的逻辑决定是否重新发起请求。

9.2 证书验证失败

当访问使用HTTPS协议的服务器时,如果服务器的证书不被信任(例如自签名证书),可能会导致证书验证失败,网络请求无法继续。可以通过实现NSURLSessionDelegate协议的URLSession:didReceiveChallenge:completionHandler:方法来处理证书验证挑战。

对于自签名证书,可以选择信任该证书:

@interface SSLManager : NSObject <NSURLSessionDelegate>
@end

@implementation SSLManager
- (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential * _Nullable))completionHandler {
    if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
        SecTrustRef serverTrust = challenge.protectionSpace.serverTrust;
        NSURLCredential *credential = [NSURLCredential credentialForTrust:serverTrust];
        completionHandler(NSURLSessionAuthChallengeUseCredential, credential);
    } else {
        completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, nil);
    }
}
@end

// 创建NSURLSession并传入委托对象
NSURL *url = [NSURL URLWithString:@"https://example.com/secure"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
SSLManager *sslManager = [[SSLManager alloc] init];
NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:sslManager delegateQueue:nil];
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request];
[dataTask resume];

在上述代码中,当遇到服务器信任挑战时,创建一个基于服务器信任的NSURLCredential对象,并使用该凭证继续请求。需要注意的是,信任自签名证书可能存在安全风险,在实际应用中应谨慎使用,并确保证书的来源可靠。

9.3 内存管理问题

在处理大量数据的网络请求(如大文件的下载或上传)时,可能会遇到内存管理的问题。对于下载任务,NSURLSessionDownloadTask会将文件下载到临时位置,在任务完成后及时将文件移动到最终位置可以避免内存占用过高。对于数据任务,如果返回的数据量较大,可以考虑分块处理数据,而不是一次性加载全部数据到内存。

例如,在数据任务的委托方法URLSession:dataTask:didReceiveData:中,可以对数据进行分块处理:

@interface DataHandler : NSObject <NSURLSessionDataDelegate>
@property (nonatomic, strong) NSMutableData *receivedData;
@end

@implementation DataHandler
- (instancetype)init {
    self = [super init];
    if (self) {
        _receivedData = [NSMutableData data];
    }
    return self;
}
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data {
    [self.receivedData appendData:data];
    // 这里可以对self.receivedData进行分块处理,例如写入文件等
}
@end

// 创建NSURLSession并传入委托对象
NSURL *url = [NSURL URLWithString:@"https://example.com/largeData"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
DataHandler *dataHandler = [[DataHandler alloc] init];
NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:dataHandler delegateQueue:nil];
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request];
[dataTask resume];

在上述代码中,使用NSMutableData逐步接收数据,并可以在适当的时候对其进行分块处理,如写入文件,以避免一次性加载大量数据导致内存问题。

通过解决这些常见问题,可以使基于NSURLSession的网络编程更加稳定和可靠。