AFNetworking 2.0 responseObject处理

时间:2013-11-15 20:57:08

标签: ios objective-c afnetworking afnetworking-2

我有一个问题。也许2个问题。

第一个涉及这段代码:

-(void)isLoggedIn {
NSString *urlString = [NSString stringWithFormat:@"%@%@%@", kBaseURL, kAPI, kUserIsLogged];
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
manager.responseSerializer = [AFJSONResponseSerializer serializerWithReadingOptions:NSJSONReadingAllowFragments];
[manager GET:urlString parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
    NSLog(@"JSON: %@", responseObject);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
    NSLog(@"Error: %@", error);
}];
}

我希望这个方法不是一个空白,我希望它返回一个BOOL或者我可以使用的东西:用户登录>好的,在应用程序的某个地方做点什么;用户未登录>确定将用户提示到可以引入其凭据并登录的屏幕。

第二个问题与这段代码有关:

-(void)detail {
NSString *urlString = [NSString stringWithFormat:@"%@%@%@", kBaseURL, kAPI, kUser];
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
manager.responseSerializer = [AFJSONResponseSerializer serializerWithReadingOptions:NSJSONReadingAllowFragments];
[manager GET:urlString parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
    NSLog(@"JSON: %@", responseObject);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
    NSLog(@"Error: %@", error);
}];
}

我想这个方法给我一个带有解析JSON的NSArray,我可以在任何地方使用它。

此方法包含在名为NetworkModel(NSObject子类)的模型中。我只是想知道为什么我应该在块里面做所有事情。我应该在整个地方与网络进行交互,而不是在一个独特的地方/类中组织。有没有办法做到这一点。我不希望每个ViewController有5行请求。我想定义我可以从任何ViewController调用的方法(显然在需要的地方导入头文件)。

有什么建议吗? 谢谢你的建议!

4 个答案:

答案 0 :(得分:3)

您可以使用两种方法执行此操作,一种更改方法以使用块或使用信号量等待异步请求:

使用信号量(THE UGLY WAY)

-(BOOL)isLoggedIn {

__block BOOL isLoggedIn;
 dispatch_semaphore_t waiter = dispatch_semaphore_create(0);

NSString *urlString = [NSString stringWithFormat:@"%@%@%@", kBaseURL, kAPI, kUserIsLogged];
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
manager.responseSerializer = [AFJSONResponseSerializer serializerWithReadingOptions:NSJSONReadingAllowFragments];
[manager GET:urlString parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
    NSLog(@"JSON: %@", responseObject);
    isLoggedIn = YES;
    dispatch_semaphore_signal(waiter);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
    NSLog(@"Error: %@", error);
    isLoggedIn = NO;
    dispatch_semaphore_signal(waiter);

}];

dispatch_group_wait(waiter, DISPATCH_TIME_FOREVER);
return isLoggedIn;
}

正确的方法

-(void)isLoggedIn:(void(^)(BOOL isLoggedIn))completionBlock {
NSString *urlString = [NSString stringWithFormat:@"%@%@%@", kBaseURL, kAPI, kUserIsLogged];
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
manager.responseSerializer = [AFJSONResponseSerializer serializerWithReadingOptions:NSJSONReadingAllowFragments];
[manager GET:urlString parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
    NSLog(@"JSON: %@", responseObject);
    completionBlock(YES);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
    NSLog(@"Error: %@", error);
    completionBlock(NO);
}];
}

对于第二个问题,您可以在网络类上设置一个块变量,该变量将在请求完成或失败时被调用。

无论如何,我认为你对块不太熟悉(因为你的第二个问题),我认为在几个街区之后你会开始喜欢它们,如果你有一小段代码,你会发现代码并不复杂执行请求&解析,你也会看到它很容易调试它们并找到问题,我的意思是,当我开始使用块时,这也是我的情况。

使用块变量

@interface YourNetworkClass:NSObject {
void(^resultHandlerBlock)(NSArray *results); //this block has as param a NSArray, you can change it to whatever you want
}

-(void)setResultHandlerBlock:(void(^)(NSArray *array))handlerBlock;

@implementation YourNetworkClass

-(void)setResultHandlerBlock:(void(^)(NSArray *array))handlerBlock {
resultHandlerBlock = block;
}

-(void)anotherMethodThatDoesNetworkOperationsAndReturnsAnArray {
 ....
 resultHandlerBlock(arrayOfResults);
}


@interface AViewControllerThatUseNetwork: UIViewController {
}

@implementation AViewControllerThatUseNetwork 

-(void)initNetworkClass {
  YourNetworkClass *networkClass = [[YourNetworkClass alloc] init]; //or other init methods
  [networkClass setResultHandlerBlock:^(NSArray *results) {
   //do whatever you want with the array
}];
}

答案 1 :(得分:1)

您的方法应采用将从AFNetworking成功(或失败)块执行的块(或两个)。这种方法包含了您尝试做的异步性质。由于这种异步性,您无法向方法添加返回值。


因此,您可以将方法更改为:

-(void)isLoggedInWithCompletion:(LoginCompletionBlock)completion {
    ...

    [manager GET:urlString parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
        NSLog(@"JSON: %@", responseObject);
        completion(/* whatever parameters your block specifies */);
    } ...
}

答案 2 :(得分:1)

我回答过类似的问题。我相信你会在那里找到你的解决方案。 Passing blocks to a AFNetworking method?

一般情况下,您需要更改您认为应该从&#34获取数据的方式;如果登录返回BOOL" to"检查我是否可以登录,如果失败则在AFNetworking块中执行某些操作。

上面的链接也回答了你的第二个问题。

答案 3 :(得分:0)

所以,对情况的更新很少(感谢所有宝贵的建议......谢谢你们!)。现在我的代码看起来像这样(在我的NetworkModel.m中,显然我已经将方法添加到NetworkModel.h以便我可以在任何地方调用它):

-(void)isLoggedIn:(void(^)(BOOL isLoggedIn))completionBlock {
NSString *urlString = [NSString stringWithFormat:@"%@%@%@", kBaseURL, kAPI, kUserIsLogged];
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
manager.responseSerializer = [AFJSONResponseSerializer serializerWithReadingOptions:NSJSONReadingAllowFragments];
[manager GET:urlString parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
    NSLog(@"JSON: %@", responseObject);
    if ([[NSString stringWithFormat:@"%@", responseObject] isEqualToString:@"true"]) {
        completionBlock(YES);
    } else {
       completionBlock(NO);
    }
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
    NSLog(@"Error: %@", error);
    completionBlock(NO);
}];
}

在我的AppDelegate.h中,我已经包含了我的NetworkModel.h,在我的AppDelegate.m中,我现在有了以下代码:

[[self model] isLoggedIn:^(BOOL isLoggedIn) {
    if (isLoggedIn == YES) {
        UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Main" bundle:nil];
        UIViewController *controller = [storyboard instantiateViewControllerWithIdentifier:@"Start01"];
        [self.window setRootViewController:controller];
    } else {
        UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Main" bundle:nil];
        UIViewController *controller = [storyboard instantiateViewControllerWithIdentifier:@"Start00"];
        [self.window setRootViewController:controller];
    }    
}];

它工作得很好。但现在我有另外一个问题。当我运行应用程序时,会发生一些奇怪的事情。一旦它检索到有关用户登录状态的信息,它就会从VC(指定的InitialView Controller - 我的故事板上的Start00)跳转到VC(在我的故事板上的loggedInVC - Start01)。有没有办法不向用户展示这种跳跃?或者这是正常的行为?现在,用户会看到第一个屏幕,然后是他应该看到的屏幕(因为他已登录)。

但我想使用块的最简单方法就是在我需要的地方使用它们。而不是试图将它们封装到方法或类或其他任何东西中。一旦数据存在,我就会直接在成功或失败区域内做我所拥有的。我在想什么?