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

Objective-C网络层封装与AFNetworking源码解析

2023-05-317.7k 阅读

Objective-C网络层封装

在Objective-C开发中,网络请求是应用程序开发的重要组成部分。为了提高代码的可维护性和复用性,对网络层进行合理的封装是很有必要的。

网络层封装的目的

  1. 代码复用:避免在多个视图控制器或模块中重复编写相同的网络请求代码。例如,在一个电商应用中,无论是商品详情页面获取商品信息,还是购物车页面获取商品价格更新,都可能需要从服务器获取数据。如果不进行封装,这些相似的网络请求代码会在不同的地方重复出现,增加了代码维护成本。通过封装网络层,只需要在一个地方编写和维护这些网络请求逻辑,其他地方直接调用即可。
  2. 易于维护:当服务器接口发生变化时,只需要在网络层封装的代码中进行修改,而不需要在所有使用网络请求的地方逐一修改。比如服务器将某个接口的请求方式从GET改为POST,或者参数格式发生变化,在封装的网络层修改后,整个应用中使用该接口的地方都能自动适配。
  3. 提高可测试性:将网络层独立封装后,可以更方便地对网络请求部分进行单元测试。可以模拟不同的网络响应,验证网络请求的正确性和业务逻辑的合理性。

网络层封装的实现方式

  1. 基于单例模式 单例模式在网络层封装中应用广泛,它确保在整个应用程序中只有一个网络请求管理实例。这样可以方便地统一管理网络请求的配置,如全局的请求头设置、超时时间等。
@interface NetworkManager : NSObject
@property (nonatomic, strong) AFHTTPSessionManager *manager;
+ (instancetype)sharedManager;
@end

@implementation NetworkManager
+ (instancetype)sharedManager {
    static NetworkManager *sharedManager = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        sharedManager = [[self alloc] init];
        sharedManager.manager = [AFHTTPSessionManager manager];
        // 设置全局请求头
        [sharedManager.manager.requestSerializer setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
        // 设置超时时间
        sharedManager.manager.requestSerializer.timeoutInterval = 15.0;
    });
    return sharedManager;
}
@end

在上述代码中,NetworkManager类采用单例模式,通过sharedManager方法获取唯一实例。在实例化过程中,初始化了AFHTTPSessionManager,并设置了全局的请求头和超时时间。

  1. 请求方法封装 为了方便调用不同类型的网络请求,通常会封装GET、POST、PUT、DELETE等常见请求方法。
@implementation NetworkManager
// GET请求
- (void)GET:(NSString *)URLString
 parameters:(id)parameters
   success:(void (^)(NSURLSessionDataTask *task, id responseObject))success
   failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure {
    [self.manager GET:URLString parameters:parameters progress:nil success:success failure:failure];
}
// POST请求
- (void)POST:(NSString *)URLString
  parameters:(id)parameters
     success:(void (^)(NSURLSessionDataTask *task, id responseObject))success
     failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure {
    [self.manager POST:URLString parameters:parameters progress:nil success:success failure:failure];
}
@end

上述代码中,NetworkManager类封装了GETPOST请求方法,内部直接调用AFHTTPSessionManager的对应方法,并将请求参数、成功和失败回调传递进去。

  1. 请求队列管理 在一些复杂的应用场景中,可能需要对网络请求进行队列管理,例如限制同时并发的请求数量,或者按照一定的优先级顺序执行请求。
@interface RequestQueue : NSObject
@property (nonatomic, strong) NSMutableArray<NSURLSessionDataTask *> *taskQueue;
@property (nonatomic, assign) NSInteger maxConcurrentRequests;
- (void)addTask:(NSURLSessionDataTask *)task;
- (void)startNextTask;
@end

@implementation RequestQueue
- (instancetype)init {
    self = [super init];
    if (self) {
        self.taskQueue = [NSMutableArray array];
        self.maxConcurrentRequests = 3;
    }
    return self;
}
- (void)addTask:(NSURLSessionDataTask *)task {
    [self.taskQueue addObject:task];
    [self startNextTask];
}
- (void)startNextTask {
    if (self.taskQueue.count == 0) {
        return;
    }
    NSInteger currentActiveTasks = 0;
    for (NSURLSessionDataTask *task in self.taskQueue) {
        if (task.state == NSURLSessionTaskStateRunning) {
            currentActiveTasks++;
        }
    }
    if (currentActiveTasks < self.maxConcurrentRequests) {
        NSURLSessionDataTask *task = self.taskQueue.firstObject;
        [task resume];
        [self.taskQueue removeObject:task];
    }
}
@end

在上述代码中,RequestQueue类实现了一个简单的请求队列管理。通过addTask:方法将任务添加到队列中,并通过startNextTask方法根据当前正在运行的任务数量和最大并发请求数来决定是否启动下一个任务。

  1. 响应处理封装 在网络请求成功或失败后,对响应的处理也可以进行封装。通常会对服务器返回的状态码进行统一处理,例如判断是否登录过期,是否接口调用成功等。
@implementation NetworkManager
- (void)handleResponse:(NSURLSessionDataTask *)task
              response:(id)responseObject
                 error:(NSError *)error
              success:(void (^)(id responseObject))success
              failure:(void (^)(NSError *error))failure {
    if (error) {
        if (failure) {
            failure(error);
        }
        return;
    }
    // 假设服务器返回的响应是JSON格式,且包含status字段表示状态
    if ([responseObject isKindOfClass:[NSDictionary class]]) {
        NSNumber *status = responseObject[@"status"];
        if (status.integerValue == 200) {
            if (success) {
                success(responseObject);
            }
        } else {
            NSError *customError = [NSError errorWithDomain:@"ServerErrorDomain" code:status.integerValue userInfo:nil];
            if (failure) {
                failure(customError);
            }
        }
    }
}
@end

上述代码中,handleResponse:方法统一处理网络请求的响应。如果发生错误,直接调用失败回调;如果响应是符合预期的JSON格式且状态码为200,则调用成功回调,否则根据服务器返回的状态码创建自定义错误并调用失败回调。

AFNetworking源码解析

AFNetworking是一款广泛使用的iOS和OS X网络框架,它提供了简洁易用的API,同时在底层实现上也非常优秀。下面对AFNetworking的核心源码进行解析。

AFNetworking架构概述

AFNetworking主要由以下几个核心部分组成:

  1. 请求序列化器(Request Serializer):负责将请求参数转换为合适的格式,如JSON、URL编码等,并设置请求头。常见的请求序列化器有AFJSONRequestSerializerAFHTTPRequestSerializer等。
  2. 响应序列化器(Response Serializer):负责将服务器返回的响应数据转换为开发者期望的格式,如JSON对象、NSData、NSString等。常见的响应序列化器有AFJSONResponseSerializerAFHTTPResponseSerializerAFXMLParserResponseSerializer等。
  3. 会话管理(Session Management):通过NSURLSession进行网络请求的管理,AFURLSessionManager是AFNetworking中用于管理NSURLSession的核心类,它可以处理多个并发请求,设置请求的优先级等。
  4. 网络活动监控(Network Activity Monitoring):AFNetworking提供了网络状态监控功能,可以实时监测设备的网络连接状态,如是否连接到网络,连接类型是WiFi还是蜂窝网络等。

请求序列化器源码解析

AFJSONRequestSerializer为例,其主要功能是将请求参数序列化为JSON格式,并设置请求头。

@interface AFJSONRequestSerializer : AFHTTPRequestSerializer
@end

@implementation AFJSONRequestSerializer
- (NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request
                               withParameters:(id)parameters
                                        error:(NSError *__autoreleasing *)error {
    NSMutableURLRequest *mutableRequest = [request mutableCopy];
    if (![parameters isKindOfClass:[NSNull class]] && parameters) {
        NSError *serializationError = nil;
        NSData *jsonData = [NSJSONSerialization dataWithJSONObject:parameters options:self.writingOptions error:&serializationError];
        if (serializationError) {
            if (error) {
                *error = serializationError;
            }
            return nil;
        }
        if (!mutableRequest.HTTPBodyStream) {
            [mutableRequest setHTTPBody:jsonData];
        }
        if (![mutableRequest valueForHTTPHeaderField:@"Content-Type"]) {
            [mutableRequest setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
        }
    }
    return mutableRequest;
}
@end

requestBySerializingRequest:withParameters:error:方法中,首先对传入的请求进行可变拷贝。如果有参数,将参数序列化为JSON数据。如果序列化过程中发生错误,返回nil。如果请求没有设置HTTP体流,则将JSON数据设置为HTTP体。同时,如果请求头中没有设置Content-Type,则设置为application/json

响应序列化器源码解析

AFJSONResponseSerializer为例,其主要功能是将服务器返回的响应数据解析为JSON对象。

@interface AFJSONResponseSerializer : AFHTTPResponseSerializer
@property (nonatomic, assign) NSJSONReadingOptions readingOptions;
@end

@implementation AFJSONResponseSerializer
- (id)responseObjectForResponse:(NSURLResponse *)response
                           data:(NSData *)data
                          error:(NSError *__autoreleasing *)error {
    if (!data) {
        if (error) {
            *error = [NSError errorWithDomain:AFURLResponseSerializationErrorDomain code:AFURLResponseSerializationErrorNoData userInfo:nil];
        }
        return nil;
    }
    NSError *serializationError = nil;
    id responseObject = [NSJSONSerialization JSONObjectWithData:data options:self.readingOptions error:&serializationError];
    if (serializationError) {
        if (error) {
            *error = serializationError;
        }
        return nil;
    }
    return responseObject;
}
@end

responseObjectForResponse:data:error:方法中,首先检查响应数据是否为空,如果为空则创建一个表示无数据的错误并返回nil。然后使用NSJSONSerialization将响应数据解析为JSON对象,如果解析过程中发生错误,则返回nil并设置错误信息。

AFURLSessionManager源码解析

AFURLSessionManager是AFNetworking中管理NSURLSession的核心类,它负责创建和管理NSURLSession实例,处理请求的发送、接收和响应。

@interface AFURLSessionManager : NSObject <NSURLSessionDelegate, NSURLSessionTaskDelegate, NSURLSessionDataDelegate>
@property (nonatomic, strong, readonly) NSURLSession *session;
@property (nonatomic, strong) id <AFURLSessionManagerTaskDelegate> taskDelegate;
@property (nonatomic, assign) BOOL sessionDidInvalidate;
@property (nonatomic, strong) NSMutableDictionary<NSURLSessionTask *, AFURLSessionDataTaskCompletionHandlerBlock> *completionHandlers;
- (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration;
- (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request
                               completionHandler:(AFURLSessionDataTaskCompletionHandlerBlock)completionHandler;
@end

@implementation AFURLSessionManager
- (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration {
    self = [super init];
    if (!self) {
        return nil;
    }
    _session = [NSURLSession sessionWithConfiguration:configuration delegate:self delegateQueue:nil];
    _completionHandlers = [NSMutableDictionary dictionary];
    return self;
}
- (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request
                               completionHandler:(AFURLSessionDataTaskCompletionHandlerBlock)completionHandler {
    NSURLSessionDataTask *dataTask = [self.session dataTaskWithRequest:request];
    if (completionHandler) {
        self.completionHandlers[dataTask] = [completionHandler copy];
    }
    return dataTask;
}
// 处理响应数据的回调
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask
 didReceiveData:(NSData *)data {
    // 这里可以进行数据的实时处理,如显示下载进度等
}
// 任务完成的回调
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task
didCompleteWithError:(NSError *)error {
    AFURLSessionDataTaskCompletionHandlerBlock completionHandler = self.completionHandlers[task];
    if (completionHandler) {
        id responseObject = nil;
        NSError *serializationError = nil;
        // 根据响应序列化器解析响应数据
        responseObject = [self.responseSerializer responseObjectForResponse:task.response data:task.responseData error:&serializationError];
        if (serializationError) {
            error = serializationError;
        }
        completionHandler(task, responseObject, error);
    }
    [self.completionHandlers removeObjectForKey:task];
}
@end

initWithSessionConfiguration:方法中,创建了一个NSURLSession实例,并初始化了completionHandlers字典用于存储任务的完成回调。dataTaskWithRequest:completionHandler:方法创建一个NSURLSessionDataTask,并将完成回调存储到completionHandlers字典中。在URLSession:dataTask:didReceiveData:方法中可以对实时接收到的数据进行处理,如显示下载进度。在URLSession:task:didCompleteWithError:方法中,根据响应序列化器解析响应数据,并调用对应的完成回调,最后从completionHandlers字典中移除该任务的回调。

网络活动监控源码解析

AFNetworking通过AFNetworkReachabilityManager类实现网络活动监控。

@interface AFNetworkReachabilityManager : NSObject
@property (nonatomic, assign, readonly) AFNetworkReachabilityStatus networkReachabilityStatus;
@property (nonatomic, copy) void (^networkReachabilityStatusBlock)(AFNetworkReachabilityStatus status);
+ (instancetype)sharedManager;
- (BOOL)startMonitoring;
- (BOOL)stopMonitoring;
@end

@implementation AFNetworkReachabilityManager
+ (instancetype)sharedManager {
    static AFNetworkReachabilityManager *sharedManager = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        sharedManager = [[self alloc] initWithReachability:NULL];
    });
    return sharedManager;
}
- (instancetype)initWithReachability:(SCNetworkReachabilityRef)reachability {
    self = [super init];
    if (!self) {
        return nil;
    }
    _reachability = reachability;
    _networkReachabilityStatus = AFNetworkReachabilityStatusUnknown;
    return self;
}
- (BOOL)startMonitoring {
    SCNetworkReachabilityContext context = {0, (__bridge void *)self, NULL, NULL, NULL};
    if (!SCNetworkReachabilitySetCallback(self.reachability, AFNetworkReachabilityCallback, &context)) {
        return NO;
    }
    if (!SCNetworkReachabilityScheduleWithRunLoop(self.reachability, CFRunLoopGetMain(), kCFRunLoopDefaultMode)) {
        return NO;
    }
    AFNetworkReachabilityStatus status = [self networkReachabilityStatusForFlags:self.currentReachabilityFlags];
    self.networkReachabilityStatus = status;
    if (self.networkReachabilityStatusBlock) {
        self.networkReachabilityStatusBlock(status);
    }
    return YES;
}
- (BOOL)stopMonitoring {
    if (!self.reachability) {
        return NO;
    }
    SCNetworkReachabilityUnscheduleFromRunLoop(self.reachability, CFRunLoopGetMain(), kCFRunLoopDefaultMode);
    return YES;
}
// 网络状态变化的回调函数
static void AFNetworkReachabilityCallback(SCNetworkReachabilityRef __unused target, SCNetworkReachabilityFlags flags, void *info) {
    AFNetworkReachabilityManager *manager = (__bridge AFNetworkReachabilityManager *)info;
    AFNetworkReachabilityStatus status = [manager networkReachabilityStatusForFlags:flags];
    manager.networkReachabilityStatus = status;
    if (manager.networkReachabilityStatusBlock) {
        manager.networkReachabilityStatusBlock(status);
    }
}
- (AFNetworkReachabilityStatus)networkReachabilityStatusForFlags:(SCNetworkReachabilityFlags)flags {
    // 根据网络状态标志判断网络状态
    if (flags & kSCNetworkReachabilityFlagsReachable && !(flags & kSCNetworkReachabilityFlagsConnectionRequired)) {
        if (flags & kSCNetworkReachabilityFlagsIsWWAN) {
            return AFNetworkReachabilityStatusReachableViaWWAN;
        } else {
            return AFNetworkReachabilityStatusReachableViaWiFi;
        }
    }
    return AFNetworkReachabilityStatusNotReachable;
}
@end

AFNetworkReachabilityManager采用单例模式,通过sharedManager方法获取唯一实例。在startMonitoring方法中,设置网络状态变化的回调函数,并将其添加到主运行循环中。AFNetworkReachabilityCallback是网络状态变化的实际回调函数,它根据网络状态标志更新AFNetworkReachabilityManager的网络状态,并调用用户设置的网络状态变化回调。networkReachabilityStatusForFlags:方法根据网络状态标志判断当前的网络状态,如是否通过WiFi或蜂窝网络可达,或者不可达。

通过对Objective-C网络层封装和AFNetworking源码的深入解析,开发者可以更好地理解网络请求在iOS应用中的实现原理,从而编写出更加健壮、高效的网络代码。无论是自己封装网络层以满足特定需求,还是基于AFNetworking进行二次开发,都能从这些知识中获得启发。在实际开发中,应根据项目的规模、需求和性能要求等因素,合理选择和优化网络层的实现方式。同时,不断关注网络技术的发展,及时更新和改进网络代码,以提供更好的用户体验。