尝试在 iOS 中使用单例模式重构网络方法
Trying to refactor networking method using Singleton pattern in iOS
我的应用程序中有以下方法,它使用 NSURLSession
从网络服务中以 JSON 格式提取电影数据:
- (void) downloadMovieData {
//this is just a visual cue to show that processing is being done
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
//creating the request
NSURL *url = [NSURL URLWithString:kMovieURL];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
//creating the session
self.config = [NSURLSessionConfiguration defaultSessionConfiguration];
self.session = [NSURLSession sessionWithConfiguration:self.config];
//the object that makes the call to the web service using the request and the session, and returns a response or an error
NSURLSessionDataTask *task = [self.session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
//At this point a response has been received, so we can turn the indicator off
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
//I am casting the response to an NSHTTPURLResponse so I can check the status code of the response
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
//a status code of 200 means a successful connection with the web service
if (httpResponse.statusCode == 200) {
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"Success!");
//I send the data that was received from the response to the method so that the JSON data is extracted
[self populateArray:data];
});
} else {
NSString *result = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(@"Received HTTP %ld: %@", (long)httpResponse.statusCode, result);
}
}];
[task resume];
}
- (void) populateArray: (NSData *)data {
NSError *jsonError;
NSDictionary *response =[NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:&jsonError];
if (response) {
self.movieObjects = response[@"movies"];
NSLog(@"The movie objects are: %@", self.movieObjects);
[self.tableView reloadData];
} else {
NSLog(@"ERROR: %@", jsonError);
}
}
以上代码运行良好。没有问题。但是,我现在想做的是重构我的代码,以便将我的所有网络代码都放在包含我的 UITableView
委托方法的 class 中,我想将代码移动到一个单独的 class 它使用 Singleton 方法来更好地分离代码。我有以下框架代码,如下所示:
#import "Networker.h"
@implementation Networker
+ (NSURLSession *)dataSession {
static NSURLSession *session = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
});
return session;
}
+ (void)fetchContentsOfURL:(NSURL *)url completion:(void (^)(NSData *data, NSError *error)) completionHandler {
NSURLSessionDataTask *dataTask = [[self dataSession] dataTaskWithURL:url
completionHandler: ^(NSData *data, NSURLResponse *response, NSError *error) {
if (completionHandler == nil) return;
if (error) {
completionHandler(nil, error);
return;
}
completionHandler(data, nil);
}];
[dataTask resume];
}
我完全可以理解 Singleton class。这不是问题。我的问题是了解如何理解此方法的 "completion handler" 部分:
+ (void)fetchContentsOfURL:(NSURL *)url completion:(void (^)(NSData *data, NSError *error)) completionHandler {}
我想做的是将方法 "downloadMovieData" 中的代码移动到 "fetchContentsOfURL" 和 return 一个 NSData
对象中然后我可以在我的调用 class 中使用它来填充 UITableView。但是,在这样做时,我想确保我了解这个新方法的 "completionHandler" 部分发生了什么。我该怎么做?
你不能移动它所以它 returns,因为它是异步的。从技术上讲你可以,但你会在等待时阻塞主线程,这很糟糕。
相反,您只需将当前代码替换为对单例的调用,并在完成块中调用其他方法来处理数据。
有两点需要注意:
- 最好在单例调用某人完成块之前将其分派给 main,这样您就不需要该函数的所有用户都记住这样做
- 不需要是单例,你可以在每次需要的时候实例化一个副本class或者使用依赖注入来代替,这两种方式通常都能提供更好的架构
我的应用程序中有以下方法,它使用 NSURLSession
从网络服务中以 JSON 格式提取电影数据:
- (void) downloadMovieData {
//this is just a visual cue to show that processing is being done
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
//creating the request
NSURL *url = [NSURL URLWithString:kMovieURL];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
//creating the session
self.config = [NSURLSessionConfiguration defaultSessionConfiguration];
self.session = [NSURLSession sessionWithConfiguration:self.config];
//the object that makes the call to the web service using the request and the session, and returns a response or an error
NSURLSessionDataTask *task = [self.session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
//At this point a response has been received, so we can turn the indicator off
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
//I am casting the response to an NSHTTPURLResponse so I can check the status code of the response
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
//a status code of 200 means a successful connection with the web service
if (httpResponse.statusCode == 200) {
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"Success!");
//I send the data that was received from the response to the method so that the JSON data is extracted
[self populateArray:data];
});
} else {
NSString *result = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(@"Received HTTP %ld: %@", (long)httpResponse.statusCode, result);
}
}];
[task resume];
}
- (void) populateArray: (NSData *)data {
NSError *jsonError;
NSDictionary *response =[NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:&jsonError];
if (response) {
self.movieObjects = response[@"movies"];
NSLog(@"The movie objects are: %@", self.movieObjects);
[self.tableView reloadData];
} else {
NSLog(@"ERROR: %@", jsonError);
}
}
以上代码运行良好。没有问题。但是,我现在想做的是重构我的代码,以便将我的所有网络代码都放在包含我的 UITableView
委托方法的 class 中,我想将代码移动到一个单独的 class 它使用 Singleton 方法来更好地分离代码。我有以下框架代码,如下所示:
#import "Networker.h"
@implementation Networker
+ (NSURLSession *)dataSession {
static NSURLSession *session = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
});
return session;
}
+ (void)fetchContentsOfURL:(NSURL *)url completion:(void (^)(NSData *data, NSError *error)) completionHandler {
NSURLSessionDataTask *dataTask = [[self dataSession] dataTaskWithURL:url
completionHandler: ^(NSData *data, NSURLResponse *response, NSError *error) {
if (completionHandler == nil) return;
if (error) {
completionHandler(nil, error);
return;
}
completionHandler(data, nil);
}];
[dataTask resume];
}
我完全可以理解 Singleton class。这不是问题。我的问题是了解如何理解此方法的 "completion handler" 部分:
+ (void)fetchContentsOfURL:(NSURL *)url completion:(void (^)(NSData *data, NSError *error)) completionHandler {}
我想做的是将方法 "downloadMovieData" 中的代码移动到 "fetchContentsOfURL" 和 return 一个 NSData
对象中然后我可以在我的调用 class 中使用它来填充 UITableView。但是,在这样做时,我想确保我了解这个新方法的 "completionHandler" 部分发生了什么。我该怎么做?
你不能移动它所以它 returns,因为它是异步的。从技术上讲你可以,但你会在等待时阻塞主线程,这很糟糕。
相反,您只需将当前代码替换为对单例的调用,并在完成块中调用其他方法来处理数据。
有两点需要注意:
- 最好在单例调用某人完成块之前将其分派给 main,这样您就不需要该函数的所有用户都记住这样做
- 不需要是单例,你可以在每次需要的时候实例化一个副本class或者使用依赖注入来代替,这两种方式通常都能提供更好的架构