使用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);
        }
    }];
}

10-04 11:34
查看更多