AFNetworking 2.0 响应对象处理

Posted

技术标签:

【中文标题】AFNetworking 2.0 响应对象处理【英文标题】:AFNetworking 2.0 responseObject handling 【发布时间】:2013-11-15 20:57:08 【问题描述】:

我有一个问题。也许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 调用的方法(显然在需要的地方导入头文件)。

有什么建议吗? 谢谢指教!

【问题讨论】:

【参考方案1】:

您可以使用 2 种方法来做到这一点,一种是更改您的方法以使用块或使用信号量来等待异步请求:

使用信号量(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);
];

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

无论如何,我认为您对块不太熟悉(因为您的第二个问题),并且我认为在几个块之后您会开始喜欢它们,并且如果您的 sn-ps 很小,您会发现代码并不复杂执行请求和解析的代码,您还会发现调试它们并找到问题非常容易,我的意思是,这也是我开始使用块时的情况。

使用块变量

@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
];

【讨论】:

我会看看这个。关于我对积木的熟悉程度,您是完全正确的。此外,当我需要数据时,我习惯于拥有数据,而这种异步的东西现在让我头疼..但我会习惯的。我只需要先了解一下:这就是为什么我问了一个可能是愚蠢的问题。 那么,关于第二个问题,你建议我做一个块变量,然后,进入成功块,make: blockVariable = responceObject? 非常感谢!伟大的!非常有用的建议! @user1447316 很高兴我能帮上忙。 是否缺少')'?那么它应该看起来像这样吗? +(void)registerLink:(void(^)(BOOL isLoggedIn))completionBlock【参考方案2】:

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

一般来说,您需要将您认为应该获取数据的方式从“如果登录返回 BOOL”更改为“检查我是否可以登录,如果登录失败,请在 AFNetworking 块中执行某些操作。

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

【讨论】:

有趣的建议!谢谢!嗯..我的问题是,如果我“检查我是否可以登录”,我的服务器将根据我是否实际登录在成功块中回答 TRUE 或 FALSE。我该怎么处理?如果用户是 IN 或 OUT,这将是成功的。只有当服务器由于某种原因关闭或我查询了无效链接或其他原因时才会失败。 您需要发送一个请求,让您的用户登录密码,并在登录成功与否的情况下在成功块内获得响应。比如发送a@a.com 123到服务器,服务器会返回:哎!我认识那个人!您将进入成功块。然后你可以移动到 Logged In ViewController。 谢谢!这正是我所做的。我发布了我的问题的答案。【参考方案3】:

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


因此,您将方法更改为:

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

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

【讨论】:

我知道这种异步性,这就是为什么我要求最好的方法来做到这一点,尊重这一事实。你能告诉我一个我用代码问的例子吗?感谢您的建议。【参考方案4】:

所以,关于情况的更新很少(感谢所有宝贵的建议......谢谢大家!)。现在我的代码看起来像这样(在我的 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 - 我的 Storyboard 上的 Start00)跳转到 VC(loggedInVC - 我的 Storyboard 上的 Start01)。有没有办法不向用户显示这种跳转?或者这是一种正常的行为?现在用户会先看到第一个屏幕,然后是他应该实际看到的屏幕(因为他已经登录)。

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

【讨论】:

以上是关于AFNetworking 2.0 响应对象处理的主要内容,如果未能解决你的问题,请参考以下文章

AFNetworking 2.0 和响应序列化选项

具有大型 JSON 响应的 AFNetworking 2.0

用于 POST 的 AFNetworking 2.0 自定义标头

如何从 AFnetworking 2.0 操作/NSURLSessioNDataTask 获取原始 JSON 响应?

AFNetworking 2.0 - 如何从子类 AFHTTPSessionManager 成功将响应传递给另一个类

使用 AFNetworking 2.0 处理后台任务