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

Objective-C中的Alamofire网络请求库详解

2021-08-204.4k 阅读

Alamofire 简介

Alamofire 是 iOS 和 macOS 开发中一款极为流行且强大的网络请求库,基于 AFNetworking 进行封装,为 Objective-C 开发者提供了更加简洁、优雅且功能丰富的网络请求解决方案。它极大地简化了网络请求的过程,使得开发者可以更专注于业务逻辑的实现,而无需过多关注底层网络细节。

安装 Alamofire

在项目中使用 Alamofire,常见的方式是通过 CocoaPods 进行安装。

  1. 安装 CocoaPods:如果尚未安装 CocoaPods,需先在终端执行以下命令进行安装:
sudo gem install cocoapods
  1. 创建 Podfile:在项目目录下创建一个名为 Podfile 的文件,可使用以下命令:
pod init
  1. 编辑 Podfile:打开 Podfile 文件,添加以下内容:
platform :ios, '9.0'
target 'YourTargetName' do
  pod 'Alamofire', '~> 5.0'
end

这里将 Alamofire 的版本指定为 5.0 左右,你可根据实际需求调整版本号。YourTargetName 需替换为你项目的实际目标名称。 4. 安装依赖:在终端进入项目目录,执行以下命令安装 Alamofire 及其依赖:

pod install

安装完成后,需使用生成的 .xcworkspace 文件打开项目,而非原来的 .xcodeproj 文件。

基本 GET 请求

在 Objective-C 中使用 Alamofire 发送 GET 请求非常简单。示例代码如下:

#import <Alamofire/Alamofire.h>

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    NSString *urlString = @"https://httpbin.org/get";
    NSDictionary *parameters = @{@"param1": @"value1", @"param2": @"value2"};
    
    [AFNetworkActivityIndicatorManager sharedManager].enabled = YES;
    
    [Alamofire request:urlString
               method:.get
           parameters:parameters
            encoding:.URLEncodingDefault
             headers:nil
              success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
                  NSLog(@"GET 请求成功: %@", responseObject);
              } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
                  NSLog(@"GET 请求失败: %@", error);
              }];
}

@end

在上述代码中:

  • 首先定义了请求的 URL urlString 和请求参数 parameters
  • 通过 [AFNetworkActivityIndicatorManager sharedManager].enabled = YES; 来控制网络请求时系统状态栏的网络活动指示器,当有网络请求时,指示器会显示,请求结束则隐藏。
  • 使用 [Alamofire request:urlString method:.get parameters:parameters encoding:.URLEncodingDefault headers:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {... } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {... }] 方法发起 GET 请求。
    • request 方法的第一个参数为请求 URL。
    • method 指定请求方法为 GET。
    • parameters 为请求参数,会拼接到 URL 后面。
    • encoding 定义参数的编码方式,这里使用默认的 URL 编码。
    • headers 可设置请求头,这里设为 nil
    • success 块在请求成功时被调用,responseObject 即为服务器返回的数据,通常是 JSON 格式解析后的对象。
    • failure 块在请求失败时被调用,error 包含了失败的详细信息。

基本 POST 请求

POST 请求用于向服务器提交数据,Alamofire 同样提供了简洁的实现方式。示例如下:

#import <Alamofire/Alamofire.h>

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    NSString *urlString = @"https://httpbin.org/post";
    NSDictionary *parameters = @{@"username": @"user1", @"password": @"pass1"};
    
    [AFNetworkActivityIndicatorManager sharedManager].enabled = YES;
    
    [Alamofire request:urlString
               method:.post
           parameters:parameters
            encoding:.JSONEncodingDefault
             headers:nil
              success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
                  NSLog(@"POST 请求成功: %@", responseObject);
              } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
                  NSLog(@"POST 请求失败: %@", error);
              }];
}

@end

此代码与 GET 请求类似,但有几点不同:

  • method 设置为 .post 以表明是 POST 请求。
  • encoding 使用 .JSONEncodingDefault,这是因为 POST 请求通常将参数以 JSON 格式发送到服务器。如果服务器期望其他格式,如表单格式,可使用 .URLEncodingHTTPBody

处理响应数据

Alamofire 在请求成功的回调块中返回的 responseObject 通常是已经解析好的对象。如果服务器返回的是 JSON 数据,responseObject 会是 NSDictionaryNSArray 类型(取决于 JSON 结构)。例如:

[Alamofire request:urlString
           method:.get
       parameters:nil
        encoding:.URLEncodingDefault
         headers:nil
          success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
              if ([responseObject isKindOfClass:[NSDictionary class]]) {
                  NSDictionary *responseDict = (NSDictionary *)responseObject;
                  NSLog(@"Response Data: %@", responseDict[@"key"]);
              } else if ([responseObject isKindOfClass:[NSArray class]]) {
                  NSArray *responseArray = (NSArray *)responseObject;
                  NSLog(@"Response Array: %@", responseArray);
              }
          } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
              NSLog(@"请求失败: %@", error);
          }];

在上述代码中,根据 responseObject 的类型进行不同处理。如果是 NSDictionary,可以通过键值对获取具体的数据;如果是 NSArray,则可根据数组元素进行操作。

自定义请求头

在实际开发中,有时需要在请求中添加自定义的请求头,比如添加身份验证信息、指定数据格式等。Alamofire 可以很方便地设置请求头。示例代码如下:

#import <Alamofire/Alamofire.h>

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    NSString *urlString = @"https://httpbin.org/headers";
    NSDictionary *headers = @{@"Authorization": @"Bearer your_token",
                              @"Content-Type": @"application/json"};
    
    [AFNetworkActivityIndicatorManager sharedManager].enabled = YES;
    
    [Alamofire request:urlString
               method:.get
           parameters:nil
            encoding:.URLEncodingDefault
             headers:headers
              success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
                  NSLog(@"请求成功: %@", responseObject);
              } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
                  NSLog(@"请求失败: %@", error);
              }];
}

@end

在这个例子中,通过 headers 字典设置了 AuthorizationContent - Type 两个请求头字段。在 request 方法中,将 headers 作为参数传入,这样在请求时就会带上这些自定义的请求头。

上传文件

Alamofire 支持文件上传功能,例如上传图片、文档等。以下是上传图片的示例代码:

#import <Alamofire/Alamofire.h>
#import <UIKit/UIKit.h>

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    NSString *urlString = @"https://httpbin.org/post";
    UIImage *image = [UIImage imageNamed:@"example.jpg"];
    NSData *imageData = UIImageJPEGRepresentation(image, 0.8);
    
    [AFNetworkActivityIndicatorManager sharedManager].enabled = YES;
    
    [Alamofire upload:^(MultipartFormData * _Nonnull formData) {
        [formData appendPartWithFileData:imageData
                                    name:@"image"
                                fileName:@"example.jpg"
                                mimeType:@"image/jpeg"];
    } to:urlString
      method:.post
  parameters:nil
   encoding:.URLEncodingDefault
    headers:nil
     success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
         NSLog(@"文件上传成功: %@", responseObject);
     } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
         NSLog(@"文件上传失败: %@", error);
     }];
}

@end

在这段代码中:

  • 首先获取要上传的图片,并将其转换为 NSData 类型。
  • 使用 [Alamofire upload:^(MultipartFormData * _Nonnull formData) {... } to:urlString method:.post parameters:nil encoding:.URLEncodingDefault headers:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {... } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {... }] 方法进行文件上传。
    • upload 块中,通过 formDataappendPartWithFileData:name:fileName:mimeType: 方法将图片数据添加到表单数据中。name 是服务器端接收文件的字段名,fileName 是上传文件的名称,mimeType 是文件的 MIME 类型。
    • to 后面跟上请求的 URL。
    • method 设置为 POST,因为文件上传通常使用 POST 方法。

下载文件

Alamofire 也提供了方便的文件下载功能。以下是下载文件的示例代码:

#import <Alamofire/Alamofire.h>

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    NSString *urlString = @"https://example.com/file.pdf";
    NSURL *destinationURL = [NSURL fileURLWithPath:[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject stringByAppendingPathComponent:@"file.pdf"]];
    
    [AFNetworkActivityIndicatorManager sharedManager].enabled = YES;
    
    [Alamofire download:urlString
          to:destinationURL
          method:.get
      parameters:nil
       headers:nil
     progress:^(NSProgress * _Nonnull downloadProgress) {
         NSLog(@"下载进度: %.2f%%", downloadProgress.fractionCompleted * 100);
     } destination:^NSURL * _Nonnull(NSURL * _Nonnull targetPath, NSURLResponse * _Nonnull response) {
         return destinationURL;
     } completionHandler:^(NSURLResponse * _Nonnull response, NSURL * _Nullable filePath, NSError * _Nullable error) {
         if (!error) {
             NSLog(@"文件下载成功: %@", filePath);
         } else {
             NSLog(@"文件下载失败: %@", error);
         }
     }];
}

@end

在这个示例中:

  • 定义了要下载的文件的 URL urlString 和文件下载后的保存路径 destinationURL,这里将文件保存到应用的文档目录下。
  • 使用 [Alamofire download:urlString to:destinationURL method:.get parameters:nil headers:nil progress:^(NSProgress * _Nonnull downloadProgress) {... } destination:^NSURL * _Nonnull(NSURL * _Nonnull targetPath, NSURLResponse * _Nonnull response) {... } completionHandler:^(NSURLResponse * _Nonnull response, NSURL * _Nullable filePath, NSError * _Nullable error) {... }] 方法进行文件下载。
    • progress 块用于实时获取下载进度,通过 downloadProgress.fractionCompleted 可以得到当前下载的进度比例。
    • destination 块用于指定文件的最终保存路径,这里返回之前定义的 destinationURL
    • completionHandler 块在下载完成后被调用,根据 error 是否为 nil 判断下载是否成功。

会话管理

Alamofire 使用 AFHTTPSessionManager 来管理网络会话。可以通过 AFHTTPSessionManager 进行一些全局的配置,例如设置超时时间、请求序列化器和响应序列化器等。示例代码如下:

#import <Alamofire/Alamofire.h>

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
    manager.requestSerializer.timeoutInterval = 10; // 设置超时时间为 10 秒
    manager.responseSerializer.acceptableContentTypes = [NSSet setWithObjects:@"application/json", @"text/json", @"text/javascript", @"text/plain", nil];
    
    NSString *urlString = @"https://httpbin.org/get";
    
    [AFNetworkActivityIndicatorManager sharedManager].enabled = YES;
    
    [manager GET:urlString
      parameters:nil
        headers:nil
       progress:nil
        success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
            NSLog(@"请求成功: %@", responseObject);
        } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
            NSLog(@"请求失败: %@", error);
        }];
}

@end

在上述代码中:

  • 通过 [AFHTTPSessionManager manager] 获取一个 AFHTTPSessionManager 实例。
  • 设置 requestSerializertimeoutInterval 为 10 秒,即请求如果在 10 秒内没有响应,将会超时。
  • 设置 responseSerializeracceptableContentTypes,表示可接受的响应内容类型,这里包括 JSON 和纯文本等类型。之后通过 managerGET 方法发起请求,其用法与直接使用 Alamofirerequest 方法类似,但可以应用全局配置。

错误处理

在网络请求过程中,可能会遇到各种错误,如网络连接失败、服务器响应错误等。Alamofire 在请求失败的回调块中提供了详细的错误信息。常见的错误类型包括:

  1. 网络连接错误:如无网络、网络连接超时等。这类错误的 error 对象的 domain 通常为 NSURLErrorDomaincode 会有不同的值来表示具体错误,例如 NSURLErrorNotConnectedToInternet 表示没有连接到互联网,NSURLErrorTimedOut 表示请求超时。
  2. 服务器响应错误:例如服务器返回 404(未找到资源)、500(服务器内部错误)等状态码。在这种情况下,error 对象的 domain 通常为 AFNetworkingErrorDomaincode 会对应不同的服务器错误情况。

以下是一个更详细的错误处理示例:

[Alamofire request:urlString
           method:.get
       parameters:nil
        encoding:.URLEncodingDefault
         headers:nil
          success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
              NSLog(@"请求成功: %@", responseObject);
          } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
              if (error.domain == NSURLErrorDomain) {
                  switch (error.code) {
                      case NSURLErrorNotConnectedToInternet:
                          NSLog(@"无网络连接");
                          break;
                      case NSURLErrorTimedOut:
                          NSLog(@"请求超时");
                          break;
                      default:
                          NSLog(@"网络连接错误: %@", error);
                          break;
                  }
              } else if (error.domain == AFNetworkingErrorDomain) {
                  NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)task.response;
                  NSLog(@"服务器响应错误: 状态码 %ld", (long)httpResponse.statusCode);
              } else {
                  NSLog(@"其他错误: %@", error);
              }
          }];

在这个示例中,根据 errordomain 判断错误类型。如果是 NSURLErrorDomain,进一步根据 code 处理不同的网络连接错误;如果是 AFNetworkingErrorDomain,获取 NSHTTPURLResponse 以获取服务器返回的状态码,从而处理服务器响应错误。

并发请求

在某些场景下,可能需要同时发起多个网络请求,并在所有请求都完成后进行统一处理。Alamofire 可以通过 AFHTTPSessionManagerdataTasks 数组结合 NSOperationQueue 来实现并发请求。示例代码如下:

#import <Alamofire/Alamofire.h>

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
    
    NSArray *urlStrings = @[@"https://httpbin.org/get", @"https://httpbin.org/post"];
    NSMutableArray<NSURLSessionDataTask *> *tasks = [NSMutableArray array];
    
    for (NSString *urlString in urlStrings) {
        NSURLSessionDataTask *task = [manager GET:urlString
                                         parameters:nil
                                           headers:nil
                                          progress:nil
                                           success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
                                               NSLog(@"请求 %@ 成功: %@", urlString, responseObject);
                                           } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
                                               NSLog(@"请求 %@ 失败: %@", urlString, error);
                                           }];
        [tasks addObject:task];
    }
    
    [AFNetworkActivityIndicatorManager sharedManager].enabled = YES;
    
    NSCountDownLatch *latch = [[NSCountDownLatch alloc] initWithCount:tasks.count];
    for (NSURLSessionDataTask *task in tasks) {
        [task setCompletionBlock:^{
            [latch countDown];
        }];
    }
    
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        [latch wait];
        dispatch_async(dispatch_get_main_queue(), ^{
            NSLog(@"所有请求已完成");
            // 在这里进行所有请求完成后的统一处理
        });
    });
}

@end

在上述代码中:

  • 首先创建了一个 AFHTTPSessionManager 实例 manager
  • 定义了一个包含多个请求 URL 的数组 urlStrings,并创建一个可变数组 tasks 用于存储每个请求的 NSURLSessionDataTask
  • 通过循环发起多个 GET 请求,并将每个请求的 NSURLSessionDataTask 添加到 tasks 数组中。
  • 使用 NSCountDownLatch 来等待所有请求完成。NSCountDownLatch 的初始计数值设置为请求任务的数量,每个任务完成时调用 [latch countDown] 使计数值减一。
  • 通过 dispatch_async 在后台队列等待所有任务完成([latch wait]),然后在主线程进行所有请求完成后的统一处理。

缓存策略

Alamofire 支持多种缓存策略,通过设置 AFHTTPSessionManagerrequestSerializercachePolicy 来实现。常见的缓存策略有:

  1. NSURLRequestUseProtocolCachePolicy:这是默认的缓存策略,它会遵循协议规定的缓存行为,例如 HTTP 协议的缓存头信息。
  2. NSURLRequestReloadIgnoringLocalCacheData:忽略本地缓存,每次都从服务器重新加载数据。
  3. NSURLRequestReturnCacheDataElseLoad:先尝试从本地缓存获取数据,如果缓存不存在则从服务器加载。
  4. NSURLRequestReturnCacheDataDontLoad:只从本地缓存获取数据,如果缓存不存在则不进行网络请求,直接返回错误。

以下是设置缓存策略的示例代码:

#import <Alamofire/Alamofire.h>

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
    manager.requestSerializer.cachePolicy = NSURLRequestReturnCacheDataElseLoad;
    
    NSString *urlString = @"https://httpbin.org/get";
    
    [AFNetworkActivityIndicatorManager sharedManager].enabled = YES;
    
    [manager GET:urlString
      parameters:nil
        headers:nil
       progress:nil
        success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
            NSLog(@"请求成功: %@", responseObject);
        } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
            NSLog(@"请求失败: %@", error);
        }];
}

@end

在这个示例中,将缓存策略设置为 NSURLRequestReturnCacheDataElseLoad,这样在发起请求时,首先会尝试从本地缓存获取数据,如果缓存中没有相关数据,则会从服务器加载。

与 JSON 序列化和反序列化的集成

Alamofire 与 JSON 的序列化和反序列化集成得非常好。默认情况下,当服务器返回 JSON 数据时,Alamofire 会自动将其解析为 NSDictionaryNSArray 类型的对象。在发送数据时,如果使用 JSONEncodingDefault 编码,会将 NSDictionaryNSArray 类型的参数转换为 JSON 格式的字符串发送到服务器。

例如,假设服务器期望接收一个包含用户信息的 JSON 对象,以下是如何构造并发送该请求:

#import <Alamofire/Alamofire.h>

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    NSString *urlString = @"https://httpbin.org/post";
    NSDictionary *parameters = @{@"user": @{@"name": @"John", @"age": @30}};
    
    [AFNetworkActivityIndicatorManager sharedManager].enabled = YES;
    
    [Alamofire request:urlString
               method:.post
           parameters:parameters
            encoding:.JSONEncodingDefault
             headers:nil
              success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
                  NSLog(@"请求成功: %@", responseObject);
              } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
                  NSLog(@"请求失败: %@", error);
              }];
}

@end

在上述代码中,parameters 是一个嵌套的 NSDictionary,通过 JSONEncodingDefault 编码后,会以 JSON 格式发送到服务器。当服务器返回 JSON 数据时,Alamofire 会自动将其解析为合适的对象类型,方便在成功回调中处理。

处理重定向

Alamofire 可以自动处理 HTTP 重定向。默认情况下,AFHTTPSessionManager 会遵循服务器返回的重定向指令,继续发起请求到新的 URL。如果需要自定义重定向处理逻辑,可以通过设置 AFHTTPSessionManagerredirectResponseBlock 来实现。示例代码如下:

#import <Alamofire/Alamofire.h>

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
    manager.redirectResponseBlock = ^NSURLRequest * _Nullable(NSURLSession * _Nonnull session, NSURLRequest * _Nonnull request, NSURLResponse * _Nonnull redirectResponse) {
        NSLog(@"重定向到: %@", redirectResponse.URL);
        // 这里可以根据重定向的 URL 进行一些自定义处理,例如修改请求头
        return request;
    };
    
    NSString *urlString = @"https://httpbin.org/redirect/1";
    
    [AFNetworkActivityIndicatorManager sharedManager].enabled = YES;
    
    [manager GET:urlString
      parameters:nil
        headers:nil
       progress:nil
        success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
            NSLog(@"请求成功: %@", responseObject);
        } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
            NSLog(@"请求失败: %@", error);
        }];
}

@end

在上述代码中,通过设置 redirectResponseBlock,在每次遇到重定向时,会打印出重定向的 URL。同时,该块返回 request,表示继续按照默认行为处理重定向。如果需要修改请求(例如添加新的请求头),可以在返回 request 之前对其进行修改。

总结

Alamofire 作为 Objective - C 开发中强大的网络请求库,提供了丰富的功能和简洁的接口,涵盖了基本请求、文件上传下载、会话管理、错误处理、并发请求等多个方面。通过合理使用 Alamofire 的各种特性,开发者能够高效地实现复杂的网络功能,提升应用的性能和用户体验。在实际项目中,需要根据具体需求选择合适的请求方法、配置缓存策略、处理错误等,以确保网络请求的稳定性和可靠性。希望本文对 Alamofire 的详细介绍能帮助你在 Objective - C 项目中更好地使用网络请求功能。

以上内容涵盖了 Alamofire 在 Objective - C 中的众多方面,从基础使用到深入的功能特性,都进行了详细阐述并提供了相应代码示例,相信能满足你对 Alamofire 学习和应用的需求。如果你在使用过程中遇到任何问题,欢迎随时查阅相关文档或进一步探索 Alamofire 的开源代码,以深入理解其实现原理和扩展功能。