【问题标题】:How to wait then perform an action based on HTTPrequest response iOS如何等待然后根据 HTTPrequest 响应执行操作 iOS
【发布时间】:2014-01-16 14:09:10
【问题描述】:

我的班级使用 HTTP Post 向 Twitter 发布推​​文

这里有一些代码 PostTweet.h

@interface PostTweet : NSObject
- (void)postMyTweet;
@end

PostTweet.m

- (void)postMyTweet 
{

    accountStore = [[ACAccountStore alloc] init];
    accountType = [accountStore accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierTwitter];
    [accountStore requestAccessToAccountsWithType:accountType options:nil completion:^(BOOL granted, NSError *error)
     {
         if (granted)
         {
             allAccounts = [accountStore accountsWithAccountType:accountType];

             if ([allAccounts count] > 0)
             {
                 userAccount = [allAccounts objectAtIndex:0];
                 userName = userAccount.username;
                 NSURL * reqURL = [NSURL URLWithString:ENDPOINT_MEDIA_UPLOAD];
                 NSDictionary * parameter = [NSDictionary dictionaryWithObject:tweetTitle forKey:@"status"];

                 SLRequest *twitterInfoRequest = [SLRequest requestForServiceType:SLServiceTypeTwitter
                                                                    requestMethod:SLRequestMethodPOST
                                                                              URL:reqURL
                                                                       parameters:parameter];
                 [twitterInfoRequest addMultipartData:tweetImage withName:PARAM_MEDIA type:CONTENT_TYPE_MULTIPART_FORM_DATA filename:nil];

                 [twitterInfoRequest setAccount:userAccount];

                 [twitterInfoRequest performRequestWithHandler:^(NSData *responseData, NSHTTPURLResponse *urlResponse, NSError *error)
                  {
                      //show status after done
                      long result = [urlResponse statusCode];



                      //Let us say that every thing is ok and I got 200 response 
                      if (result == 200)
                      {
                          NSLog(@"%ld",result);
                      }




                  }
                  ];
             }
         }
         else
         {
             NSLog(@"Not authorized");
         }
     }];

}

在我的viewcontroller.m

- (void) actuallySendTweet
{
    PostTweet * pt = [[PostTweet alloc] init];

    [pt postTweet];
    NSLog(@"Done");
}

问题是:调用testMethod后,如何等待http请求响应,我可以根据响应做任何事情。

现在发生的情况是,只要我调用 testMethod,NSLog 就会立即执行,而不是等待 http 响应。

【问题讨论】:

  • 您真的不希望您的主队列等待网络响应。你应该在你的完成块(你有NSLog(@"%ld", result);)中做任何你想要的推文后操作。实际上,在异步编程中,您应该将请求的启动与服务器响应的处理分开。如果绝对必要,您可以显示一些 UI,告诉用户正在发生背景事情(例如,带有 UIActivityViewIndicator 的视图),发起请求,并让 performRequestWithHandler 的完成块将其删除。
  • 技术上的答案是“在performRequestWithHandler 中做”,但如果您正在寻找不那么抽象的答案,您必须给我们一个不那么抽象的问题。也就是说,发布这条推文后你到底想做什么?如果您告诉我们您要做什么,我们可能会更容易提出建设性的建议。
  • 我只想根据http请求结果显示完成消息。例如如果结果是 200,MBProgressHUD 会弹出成功消息(我知道这部分怎么做)。除 200 以外,将弹出一条失败的消息。 @Rob
  • 那么你应该把它放在 performRequestWithHandlercompletion 块中,而不是在你调用 postTweet 之后。尽可能简单。并且不要使用已接受答案的障碍调用。

标签: iphone ios objective-c http


【解决方案1】:

首先,如果您想协调两个不同的线程,dispatch_semaphore_t 可能比dispatch_group_t 更合适。

其次,更重要的是,你不应该采取像performRequestWithHandler这样的异步方法,以同步的方式从主队列中调用它。你永远不应该阻塞主队列。

幸运的是 performRequestWithHandler 给了我们一个 handler 块,我们可以在推文完成后使用它来执行操作。在您的 cmets 中,您说您只是想在推文之后更新您的 HUD,所以您应该这样做 performRequestWithHandler(将该 UI 更新发送回主队列,因为正如 the documentation 所说,“处理程序不能保证在任何特定线程上调用”):

- (void)postMyTweet
{
    ACAccountStore *accountStore = [[ACAccountStore alloc] init];
    ACAccountType  *accountType  = [accountStore accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierTwitter];
    [accountStore requestAccessToAccountsWithType:accountType options:nil completion:^(BOOL granted, NSError *error)
     {
         if (granted)
         {
             NSArray *allAccounts = [accountStore accountsWithAccountType:accountType];

             if ([allAccounts count] > 0)
             {
                 ACAccount    *userAccount = [allAccounts objectAtIndex:0];
                 NSURL        *reqURL      = [NSURL URLWithString:ENDPOINT_MEDIA_UPLOAD];
                 NSDictionary *parameter   = [NSDictionary dictionaryWithObject:tweetTitle forKey:@"status"];

                 SLRequest *twitterRequest = [SLRequest requestForServiceType:SLServiceTypeTwitter
                                                                requestMethod:SLRequestMethodPOST
                                                                          URL:reqURL
                                                                   parameters:parameter];

                 [twitterRequest addMultipartData:tweetImage withName:PARAM_MEDIA type:CONTENT_TYPE_MULTIPART_FORM_DATA filename:nil];
                 [twitterRequest setAccount:userAccount];
                 [twitterRequest performRequestWithHandler:^(NSData *responseData, NSHTTPURLResponse *urlResponse, NSError *error)
                  {
                      if (error)
                          NSLog(@"tweet fail; error = %@", error);
                      else
                      {
                          long result = [urlResponse statusCode];

                          if (result == 200)
                              NSLog(@"%ld",result);
                          else
                              NSLog(@"Unexpected response: %@", [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding]]);
                      }

                      // Dispatch UI updates back to main queue

                      dispatch_async(dispatch_get_main_queue(), ^{
                          // do your MBProgressHUD stuff here
                      });
                  }];
             }
         }
         else
         {
             NSLog(@"Not authorized");
         }
     }];
}

您还问过“如何将 HTTP 响应结果传递给视图控制器?”显然,您在 performRequestWithHandler 中完成所有这些操作,在那里您拥有 HTTP 响应(和响应数据)。


如果您希望 postTweet 同步运行,那么最佳实践将要求您不要从主队列提交它(因为,冒着听起来像破记录的风险,您永远不想阻塞主队列)。但是你可以让 actuallySendTweet 从后台队列中发送这条推文,例如:

- (void) actuallySendTweet
{
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        PostTweet * pt = [[PostTweet alloc] init];

        [pt postTweetSynchronously];

        NSLog(@"Done");

        dispatch_async(dispatch_get_main_queue(), ^{
            // Now do any UI updates you want here.

            // For example, do your MBProgressHUD update here.
        });
    });
}

- (void)postTweetSynchronously
{
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);

    ACAccountStore *accountStore = [[ACAccountStore alloc] init];
    ACAccountType  *accountType  = [accountStore accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierTwitter];
    [accountStore requestAccessToAccountsWithType:accountType options:nil completion:^(BOOL granted, NSError *error)
     {
         if (granted)
         {
             NSArray *allAccounts = [accountStore accountsWithAccountType:accountType];

             if ([allAccounts count] > 0)
             {
                 ACAccount    *userAccount = [allAccounts objectAtIndex:0];
                 NSURL        *reqURL      = [NSURL URLWithString:ENDPOINT_MEDIA_UPLOAD];
                 NSDictionary *parameter   = [NSDictionary dictionaryWithObject:tweetTitle forKey:@"status"];

                 SLRequest *twitterRequest = [SLRequest requestForServiceType:SLServiceTypeTwitter
                                                                requestMethod:SLRequestMethodPOST
                                                                          URL:reqURL
                                                                   parameters:parameter];

                 [twitterRequest addMultipartData:tweetImage withName:PARAM_MEDIA type:CONTENT_TYPE_MULTIPART_FORM_DATA filename:nil];
                 [twitterRequest setAccount:userAccount];

                 [twitterRequest performRequestWithHandler:^(NSData *responseData, NSHTTPURLResponse *urlResponse, NSError *error)
                  {
                      // do whatever you want here, perhaps updating some class properties

                      // now that we're done, signal the semaphore
                      dispatch_semaphore_signal(semaphore);
                  }];
             }
         }
         else
         {
             NSLog(@"Not authorized");
             dispatch_semaphore_signal(semaphore); // make sure to signal here, too
         }
     }];

     dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
}

【讨论】:

  • 如果你回顾我的主要问题,你会注意到我正在从 viewcontroller.m 类调用 (postMyTweet)。处理向 twitter 发布推​​文的方法实际上在另一个类中。因此,当我调用它时,它正在完成工作,但 NSLog@"Done";不等待 postMyTweet 方法完成并返回 http 响应结果。这是我的问题:如何让它等待并返回响应结果,然后我可以执行任何操作。谢谢@Rob
  • @iMubarak “如何让它等待”的答案是“你可以使用信号量来做到这一点,但不建议从主队列中做到这一点。”但是我已经修改了答案,以展示如何同步发布推文,但我也演示了如何确保你不要在主队列上这样做,如果你想小心的话。
【解决方案2】:

在这里,您正在使用完成块。线程不等待块的执行。 因此,如果您希望块的执行应该在完成方法执行之前完成并处理数据,您可以使用,

dispatch_group_t

我正在为此修改你的方法,

- (void)postMyTweet 
{

    accountStore = [[ACAccountStore alloc] init];
    accountType = [accountStore accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierTwitter];

    dispatch_group_t group = dispatch_group_create();

    dispatch_group_enter(group);

    [accountStore requestAccessToAccountsWithType:accountType options:nil completion:^(BOOL granted, NSError *error)
     {
         if (granted)
         {
             allAccounts = [accountStore accountsWithAccountType:accountType];

             if ([allAccounts count] > 0)
             {
                 userAccount = [allAccounts objectAtIndex:0];
                 userName = userAccount.username;
                 NSURL * reqURL = [NSURL URLWithString:ENDPOINT_MEDIA_UPLOAD];
                 NSDictionary * parameter = [NSDictionary dictionaryWithObject:tweetTitle forKey:@"status"];

                 SLRequest *twitterInfoRequest = [SLRequest requestForServiceType:SLServiceTypeTwitter
                                                                    requestMethod:SLRequestMethodPOST
                                                                              URL:reqURL
                                                                       parameters:parameter];
                 [twitterInfoRequest addMultipartData:tweetImage withName:PARAM_MEDIA type:CONTENT_TYPE_MULTIPART_FORM_DATA filename:nil];

                 [twitterInfoRequest setAccount:userAccount];

                 [twitterInfoRequest performRequestWithHandler:^(NSData *responseData, NSHTTPURLResponse *urlResponse, NSError *error)
                  {
                      //show status after done
                      long result = [urlResponse statusCode];



                      //Let us say that every thing is ok and I got 200 response 
                      if (result == 200)
                      {
                          NSLog(@"%ld",result);
                      }


                      dispatch_group_leave(group);
                  }
                  ];
             }
         }
         else
         {
             NSLog(@"Not authorized");
             dispatch_group_leave(group);
         }
     }];

     dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
    dispatch_release(group);

}

现在你可以从中得到灵感了。

【讨论】:

  • 显然,不要在主队列中使用dispatch_group_wait
  • 如何将 HTTP 响应结果传回给 viewcontroller ..?? @Harsh
  • 有一些方法,但(正如苹果所说)我建议你编写自己的委托。
  • @Rob 您能否建议替代方案或此处需要哪些改进?它也会帮助我。
  • @Harsh 以这种方式使用group 是不标准的,它让我很不安在一个线程上进入组并从另一个线程离开它。通常,您使用信号量来协调线程,即在启动异步进程之前dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);,在异步块的末尾附近执行dispatch_semaphore_signal(semaphore);,然后从调用队列中执行dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);。不确定这是否是问题所在,但这是您通常的做法。 但永远不要从主队列中执行 wait 这些方法是异步的是有原因的。
猜你喜欢
  • 2012-02-25
  • 1970-01-01
  • 2011-07-26
  • 2021-12-04
  • 2020-03-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多