Во-первых, если вы хотите координировать две разные нити, dispatch_semaphore_t
может быть более подходящим, чем dispatch_group_t
.
Во-вторых, и что более важно, вы не должны брать асинхронный метод, такой как performRequestWithHandler
, вызывать его из основной очереди синхронно. Вы никогда должны блокировать основную очередь.
К счастью, performRequestWithHandler
дает нам блок handler
, который мы можем использовать для выполнения действий после завершения твита. В ваших комментариях вы говорите, что просто хотите обновить свой HUD после твита, поэтому вы должны сделать это performRequestWithHandler
(отправка этого обновления пользовательского интерфейса обратно в основную очередь, потому что, как сообщает 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 привести к ViewController“? Очевидно, вы все это делаете в 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);
}
Вы действительно не хотите, чтобы ваш основной очереди, чтобы ждать ответа сети.Вы должны делать любое действие после твита, которое вы хотите в своем блоке завершения (где у вас есть этот NSW (@ "% ld", result); '). Эффективно, при асинхронном программировании вы должны отделить инициирование запроса от обработки ответа с сервера. Если это абсолютно необходимо, вы можете представить некоторый пользовательский интерфейс, который сообщает пользователю, что происходит фоновый материал (например, представление с помощью «UIActivityViewIndicator»), инициировать запрос и блокировать завершение 'performRequestWithHandler' удалить его. – Rob
Технический ответ: «сделайте это в' performRequestWithHandler' », но если вы ищете менее абстрактный ответ, вы должны дать нам менее абстрактный вопрос. А именно, что именно вы хотите сделать после публикации этого твита? Нам может быть легче сделать конструктивные предложения, если вы скажете нам, что вы пытаетесь сделать. – Rob
Я просто хочу показать сообщение завершения на основе результата запроса http. Например, если результат равен 200, появится сообщение MBProgressHUD с сообщением об успехе (я знаю, как сделать эту часть). Другие, тогда 200, выйдет сообщение об ошибке. @Rob – iMubarak