使用Web服务创建第一个应用程序时,我正在使用AFNetworking用于Web服务。一切工作正常,但我不知道该如何从响应中的块中获取数据。这是我到目前为止所做的
+(WebServices *)sharedManager{
static WebServices *managerServices = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
managerServices = [[self alloc] init];
});
return managerServices;
}
-(NSArray *)firstPostService{
//1
NSURL *url = [NSURL URLWithString:BaseURLString];
//2
AFHTTPSessionManager *manager = [[AFHTTPSessionManager alloc] initWithBaseURL:url];
manager.responseSerializer = [AFHTTPResponseSerializer serializer];
NSDictionary *param = @{@"request" : @"get_pull_down_menu" , @"data" : @"0,0,3,1"};
[manager POST:@"person.php" parameters:param success:^(NSURLSessionDataTask *task, id responseObject) {
[self methodUsingJsonFromSuccessBlock:responseObject];
} failure:^(NSURLSessionDataTask *task, NSError *error) {
UIAlertView *av = [[UIAlertView alloc] initWithTitle:@"Error retrieving data" message:[error localizedDescription] delegate:nil cancelButtonTitle:@"Ok" otherButtonTitles:nil, nil];
[av show];
}];
if (list.count == 0) {
NSLog(@"Nothing in array yet!!");
}
else{
NSLog(@"Object 1 is : %@", [list objectAtIndex:1]);
}
return list;
}
- (void)methodUsingJsonFromSuccessBlock:(id)json {
// use the json
NSString *string = [NSString stringWithUTF8String:[json bytes]];
NSLog(@"This is data : %@", string);
list = [string componentsSeparatedByString:@"\n"];
NSLog(@"After sepration first object: %@", [list objectAtIndex:1]);
//NSLog(@"json from the block : %@", json);
}
我了解从不同的博客和Tut中读取的内容,该功能块是一个单独的线程,而我所做的每一件事都会完成它。我读了一些通常用于此的地方
dispatch_async(dispatch_get_main_queue(), ^{
data = [string componentsSeparatedByString:@"\n"];
//WHERE DATA IS __block NSArray * data = [[NSArray alloc] init];
});
我在函数(firstPostService)的中返回了它,但是什么也没发生。我仍然在块外得到一个空数组。请帮助我,建议我一些好阅读的东西。预先谢谢大家。
最佳答案
你说:
我需要将此数据发送给我试图在调度部分返回的视图控制器,但不允许这样做。是否有可能将数据输入到我的viewcontroller类中?
是的,有可能。但是,不,firstPostService
不应返回结果。不能因为它立即返回而已,但是POST
完成块要等到很久以后才被调用。到firstPostService
返回时,什么也没有返回。
在原始问题的结尾,您说:
我了解从不同的博客和Tut中读取的内容,该功能块是一个单独的线程,而我所做的每一件事都完成了它。我读了一些通常用于此的地方
dispatch_async(dispatch_get_main_queue(), ^{
data = [string componentsSeparatedByString:@"\n"];
//WHERE DATA IS __block NSArray * data = [[NSArray alloc] init];
});
这不是
__block
局部变量的适当模式。当处理一些同步运行的块(例如枚举方法的块)时,通常使用__block
模式。但是,尽管您可以将__block
变量与异步块一起使用,但是您几乎从不这样做(甚至尝试这样做也没有意义)。使用适当的完成块模式时,不需要任何__block
变量。因此,让我们回到原始代码示例:因此,您应该从AFNetworking中获取一个页面并自己使用完成块。当AFNetworking
POST
方法想要异步将数据返回到您的代码时,它使用了完成块模式。因此,如果您自己的firstPostService
要异步传递回数据,则应执行相同的操作。例如:
@interface WebServices ()
@property (nonatomic, strong) AFHTTPSessionManager *manager;
@end
@implementation WebServices
// note, use `instancetype` rather than actually referring to WebServices
// in the `sharedManager` method
+ (instancetype)sharedManager
{
static id sharedMyManager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedMyManager = [[self alloc] init];
});
return sharedMyManager;
}
// I'd also suggest that you init the `AFHTTPSessionManager` only once when this
// object is first instantiated, rather than doing it when `firstPostService` is
// called
- (instancetype)init
{
self = [super init];
if (self) {
NSURL *url = [NSURL URLWithString:BaseURLString];
self.manager = [[AFHTTPSessionManager alloc] initWithBaseURL:url];
self.manager.responseSerializer = [AFHTTPResponseSerializer serializer];
}
return self;
}
// Notice:
//
// 1. This now has a return type of `void`, because when it instantly returns,
// there is no data to return.
//
// 2. In order to pass the data back, we use the "completion handler" pattern.
- (void)firstPostServiceWithCompletionHandler:(void (^)(NSArray *list, NSError *error))completionHandler {
NSDictionary *param = @{@"request" : @"get_pull_down_menu" , @"data" : @"0,0,3,1"};
[self.manager POST:@"person.php" parameters:param success:^(NSURLSessionDataTask *task, id responseObject) {
NSArray *list = [self methodUsingJsonFromSuccessBlock:responseObject];
if (completionHandler) {
completionHandler(list, nil);
}
} failure:^(NSURLSessionDataTask *task, NSError *error) {
[[[UIAlertView alloc] initWithTitle:@"Error retrieving data" message:[error localizedDescription] delegate:nil cancelButtonTitle:@"Ok" otherButtonTitles:nil, nil] show];
if (completionHandler) {
completionHandler(nil, error);
}
}];
// // none of this code belongs here!!! You are dealing with asynchronous methods.
// // the `list` has not been returned by the time you get here!!! You shouldn't even
// // be using instance variable anyway!
//
// if (list.count == 0) {
//
// NSLog(@"Nothing in array yet!!");
// }
// else{
// NSLog(@"Object 1 is : %@", [list objectAtIndex:1]);
//
// }
// return list;
}
- (NSArray *)methodUsingJsonFromSuccessBlock:(NSData *)data {
// note, do not use `stringWithUTF8String` with the `bytes` of the `NSData`
// this is the right way to convert `NSData` to `NSString`:
NSString *string = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(@"This is string representation of the data : %@", string);
// Note, retire the `list` instance variable, and instead use a local variable
NSArray *list = [string componentsSeparatedByString:@"\n"];
NSLog(@"After sepration first object: %@", [list objectAtIndex:1]);
return list;
}
@end
然后,您可以像这样调用它:
[[WebServices sharedManager] firstPostServiceWithCompletionHandler:^(NSArray *list, NSError *error) {
if (error) {
// handle the error here
} else {
// use the `list` results here
}
}];
// NOTE, DO NOT USE `list` HERE. By the time you get here, `list` has not been
// returned. Only use it in the above block.
//
// In fact, you can see that if you put a `NSLog` or breakpoint here, and again, above
// where it says "use the `list` results` here", you'll see that it's running the code
// inside that block _after_ this code down here!
我建议您首先解决以上问题,首先要确保您完全了解完成块模式的正确异步技术。我们还不想使事情复杂化。在继续下面将要描述的内容之前,请确保已获取所需的数据。
但是,一旦您了解了上述内容,就该看看您的JSON解析了。您多次引用了JSON,但如果确实如此,则使用
componentsSeparatedByString
并不是解析它的正确方法。您应该使用NSJSONSerialization
。甚至更好的是,您可以让AFNetworking为您做到这一点(现在,它使它变得比所需的更加复杂,并且结果将无法正确格式化)。上面,我将您的
methodUsingJsonFromSuccessBlock
保留在此过程中,但是如果您确实在处理JSON,则应完全消除该方法。让AFNetworking为您做到这一点。您应该删除以下行:
responseSerializer = [AFHTTPResponseSerializer serializer];
默认的序列化器是
AFJSONResponseSerializer
,它是处理JSON请求时要使用的序列化器。然后不再需要
methodUsingJsonFromSuccessBlock
,因为AFNetworking将为您执行JSON转换。所以firstPostServiceWithCompletionHandler
应该看起来像:- (void)firstPostServiceWithCompletionHandler:(void (^)(NSArray *list, NSError *error))completionHandler {
NSDictionary *param = @{@"request" : @"get_pull_down_menu" , @"data" : @"0,0,3,1"};
[self.manager POST:@"person.php" parameters:param success:^(NSURLSessionDataTask *task, id responseObject) {
if (completionHandler) {
completionHandler(responseObject, nil);
}
} failure:^(NSURLSessionDataTask *task, NSError *error) {
[[[UIAlertView alloc] initWithTitle:@"Error retrieving data" message:[error localizedDescription] delegate:nil cancelButtonTitle:@"Ok" otherButtonTitles:nil, nil] show];
if (completionHandler) {
completionHandler(nil, error);
}
}];
}