面试题答案
一键面试-
定义网络请求基类
- 创建一个基类,例如
NetworkRequest
,作为所有网络请求对象的基类。它可以包含一些通用的属性和方法,如请求的URL、请求方法(GET、POST等)、请求头、以及发送请求和处理响应的通用逻辑。
@interface NetworkRequest : NSObject @property (nonatomic, strong) NSURL *url; @property (nonatomic, strong) NSString *requestMethod; @property (nonatomic, strong) NSDictionary<NSString *, NSString *> *headers; - (void)sendRequestWithCompletion:(void (^)(NSData * _Nullable data, NSError * _Nullable error))completion; @end @implementation NetworkRequest - (void)sendRequestWithCompletion:(void (^)(NSData * _Nullable data, NSError * _Nullable error))completion { NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:self.url]; request.HTTPMethod = self.requestMethod; [self.headers enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull key, NSString * _Nonnull obj, BOOL * _Nonnull stop) { [request setValue:obj forHTTPHeaderField:key]; }]; NSURLSessionDataTask *task = [[NSURLSession sharedSession] dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) { completion(data, error); }]; [task resume]; } @end
- 创建一个基类,例如
-
定义具体的网络请求子类
- 根据不同类型的网络请求,创建继承自
NetworkRequest
的子类。例如,假设我们有登录请求和获取用户信息请求。
@interface LoginRequest : NetworkRequest - (instancetype)initWithUsername:(NSString *)username password:(NSString *)password; @end @implementation LoginRequest - (instancetype)initWithUsername:(NSString *)username password:(NSString *)password { self = [super init]; if (self) { self.url = [NSURL URLWithString:@"https://example.com/login"]; self.requestMethod = @"POST"; NSMutableDictionary<NSString *, NSString *> *body = [NSMutableDictionary dictionary]; body[@"username"] = username; body[@"password"] = password; NSString *bodyString = [body queryString];// 假设这里有一个将字典转成query string的category方法 self.headers = @{@"Content-Type": @"application/x-www-form-urlencoded", @"Content-Length": @(bodyString.length).stringValue}; [self setValue:bodyString forHTTPBody:[bodyString dataUsingEncoding:NSUTF8StringEncoding]]; } return self; } @end @interface UserInfoRequest : NetworkRequest - (instancetype)initWithUserId:(NSString *)userId; @end @implementation UserInfoRequest - (instancetype)initWithUserId:(NSString *)userId { self = [super init]; if (self) { self.url = [NSURL URLWithString:[NSString stringWithFormat:@"https://example.com/user/%@", userId]]; self.requestMethod = @"GET"; self.headers = @{@"Content-Type": @"application/json"}; } return self; } @end
- 根据不同类型的网络请求,创建继承自
-
创建工厂类
- 创建一个工厂类,例如
NetworkRequestFactory
,用于创建不同类型的网络请求对象。
@interface NetworkRequestFactory : NSObject + (NetworkRequest *)createRequestWithType:(NSString *)type parameters:(NSDictionary *)parameters; @end @implementation NetworkRequestFactory + (NetworkRequest *)createRequestWithType:(NSString *)type parameters:(NSDictionary *)parameters { if ([type isEqualToString:@"login"]) { NSString *username = parameters[@"username"]; NSString *password = parameters[@"password"]; return [[LoginRequest alloc] initWithUsername:username password:password]; } else if ([type isEqualToString:@"userInfo"]) { NSString *userId = parameters[@"userId"]; return [[UserInfoRequest alloc] initWithUserId:userId]; } return nil; } @end
- 创建一个工厂类,例如
-
错误处理与资源管理
- 错误处理:
- 在
NetworkRequest
基类的sendRequestWithCompletion
方法中,当网络请求失败时,通过completion
block返回NSError
对象,告知调用者错误信息。例如:
- (void)sendRequestWithCompletion:(void (^)(NSData * _Nullable data, NSError * _Nullable error))completion { NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:self.url]; request.HTTPMethod = self.requestMethod; [self.headers enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull key, NSString * _Nonnull obj, BOOL * _Nonnull stop) { [request setValue:obj forHTTPHeaderField:key]; }]; NSURLSessionDataTask *task = [[NSURLSession sharedSession] dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) { if (error) { completion(nil, error); } else { // 还可以在这里根据HTTP状态码进行更细粒度的错误处理 NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response; if (httpResponse.statusCode >= 400) { NSError *customError = [NSError errorWithDomain:@"NetworkErrorDomain" code:httpResponse.statusCode userInfo:nil]; completion(nil, customError); } else { completion(data, nil); } } }]; [task resume]; }
- 在
- 资源管理:
- 在网络请求对象创建过程中,尽量避免内存泄漏。例如,在
NetworkRequest
子类的初始化方法中,正确设置属性和初始化资源,并且在对象销毁时(如果有需要手动释放的资源),在dealloc
方法中释放资源。对于NSURLSessionDataTask
,当任务完成(成功或失败),系统会自动管理其相关资源,一般不需要额外手动释放。
- 在网络请求对象创建过程中,尽量避免内存泄漏。例如,在
- 错误处理:
-
优化代码结构提高可维护性和可扩展性
- 可维护性:
- 将网络请求相关的代码集中在
NetworkRequest
基类和其具体子类以及NetworkRequestFactory
中,使得代码结构清晰,易于理解和维护。如果需要修改网络请求的通用逻辑,只需要在NetworkRequest
基类中修改;如果需要修改某一种具体请求的逻辑,只需要在对应的子类中修改。
- 将网络请求相关的代码集中在
- 可扩展性:
- 当需要添加新的网络请求类型时,只需要创建新的继承自
NetworkRequest
的子类,并在NetworkRequestFactory
的createRequestWithType:parameters:
方法中添加相应的创建逻辑即可,不需要对现有的其他请求类和工厂类的主要逻辑进行大规模修改。同时,如果需要对网络请求添加新的通用功能,如添加请求超时处理,只需要在NetworkRequest
基类中添加相关逻辑,所有子类都会继承该功能。
- 当需要添加新的网络请求类型时,只需要创建新的继承自
- 可维护性: