Я знаком с использованием AsyncTask
в Android: создайте подкласс, вызовите execute
в экземпляре подкласса и onPostExecute
вызывается в потоке пользовательского интерфейса или в главном потоке. Что эквивалентно в iOS?
IOS/Objective-C эквивалент Android AsyncTask
Ответ 1
Ответ на исходный вопрос:
Grand Central Dispatch (GCD) предлагает механизм для выполнения задач в фоновом режиме, хотя он работает по-разному, чем AsyncTask. Чтобы выполнить что-то асинхронно, вам просто нужно создать очередь (например, поток), а затем передать блок в dispatch_async()
для выполнения в фоновом режиме. Я нахожу его более аккуратным, чем AsyncTask, так как нет никакого подкласса; это более или менее plug-and-play везде, где у вас есть код, который вы хотите выполнить в фоновом режиме. Пример:
dispatch_queue_t queue = dispatch_queue_create("com.yourdomain.yourappname", NULL);
dispatch_async(queue, ^{
//code to be executed in the background
});
Другие пункты:
1) Обратные вызовы
Если вы хотите выполнить задачу в фоновом режиме и обновить пользовательский интерфейс (или сделать что-то в другом потоке), когда фоновая задача будет выполнена, вы можете просто вложить вызовы отправки:
dispatch_queue_t queue = dispatch_queue_create("com.yourdomain.yourappname", NULL);
dispatch_async(queue, ^{
//code to be executed in the background
dispatch_async(dispatch_get_main_queue(), ^{
//code to be executed on the main thread when background task is finished
});
});
2) Глобальные очереди
При создании очереди вы также можете использовать функцию dispatch_get_global_queue()
для получения глобальной очереди отправки с определенным приоритетом (например, DISPATCH_QUEUE_PRIORITY_HIGH
). Эти очереди универсальны и полезны, когда вы хотите назначить несколько задач одному и тому же потоку/очереди. Обратите внимание, что память полностью управляется iOS.
3) Память
Иногда возникает некоторая путаница в отношении управления памятью и очередей отправки, поскольку они имеют свои собственные функции dispatch_retain
/dispatch_release
. Однако будьте уверены, что они рассматриваются как объекты Objective-C ARC, поэтому вам не нужно беспокоиться о вызове этих функций. Ссылаясь на rob mayoff отличный ответ относительно GCD и ARC, вы можете увидеть, что документация описывает эквивалентность очередей GCD с объектами Objective-C:
* By default, libSystem objects such as GCD and XPC objects are declared as
* Objective-C types when building with an Objective-C compiler. This allows
* them to participate in ARC, in RR management by the Blocks runtime and in
* leaks checking by the static analyzer, and enables them to be added to Cocoa
* collections.
*
* NOTE: this requires explicit cancellation of dispatch sources and xpc
* connections whose handler blocks capture the source/connection object,
* resp. ensuring that such captures do not form retain cycles (e.g. by
* declaring the source as __weak).
*
* To opt-out of this default behavior, add -DOS_OBJECT_USE_OBJC=0 to your
* compiler flags.
*
* This mode requires a platform with the modern Objective-C runtime, the
* Objective-C GC compiler option to be disabled, and at least a Mac OS X 10.8
* or iOS 6.0 deployment target.
4) Несколько задач/блоков
Я добавлю, что GCD имеет интерфейс группировки, который поддерживает синхронизацию нескольких асинхронных блоков, если задача не может продолжаться до тех пор, пока не завершится несколько асинхронных действий. Jörn Eyrich и ɲeuroburɳ предоставляют щедрые объяснения этой темы здесь. Если вам нужна эта функциональность, я бы очень рекомендовал занять несколько минут, чтобы внимательно прочитать их ответы и понять различия между ними.
documentation содержит множество информации по этой теме, если вы так склонны.
Ответ 2
В iOS нет классов, но вы можете имитировать его с помощью очередей. Вы можете позвонить:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//Your code to execute in background...
});
для задач async и внутри вашего асинхронного кода выполните следующую очередь, чтобы что-то сделать в представлении...:
dispatch_async(dispatch_get_main_queue(), ^{
//Your code to execute on UIthread (main thread)
});
Затем, используя эти две очереди, вы можете создать класс asyncTask, добавить этот класс в свой проект, чтобы реализовать их:
//
// AsyncTask.h
//
// Created by Mansour Boutarbouch Mhaimeur on 25/10/13.
//
#import <Foundation/Foundation.h>
@interface AsyncTask : NSObject
- (void) executeParameters: (NSArray *) params;
- (void) preExecute;
- (NSInteger) doInBackground: (NSArray *) parameters;
- (void) postExecute: (NSInteger) result;
@end
//
// AsyncTask.m
//
// Created by Mansour Boutarbouch Mhaimeur on 25/10/13.
//
#import "AsyncTask.h"
@implementation AsyncTask
- (void) executeParameters: (NSArray *) params{
[self preExecute];
__block NSInteger result;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
result = [self doInBackground:params];
dispatch_async(dispatch_get_main_queue(), ^{
[self postExecute:result];
});
});
}
- (void) preExecute{
//Method to override
//Run on main thread (UIThread)
}
- (NSInteger) doInBackground: (NSArray *) parameters{
//Method to override
//Run on async thread (Background)
return 0;
}
- (void) postExecute: (NSInteger) result{
//Method to override
//Run on main thread (UIThread)
}
@end
Это пример, который я использую в проекте:
#import "AsyncTask.h"
#import "Chat.h"
@interface SendChatTask : AsyncTask{
NSArray * chatsNotSent;
}
@end
#import "SendChatTask.h"
@implementation SendChatTask
- (void) preExecute{
//Method to override
}
- (NSInteger) doInBackground: (NSArray *) parameters{
//Method to override
NSString *sendChatsURL = [NSString stringWithFormat:@"%@%@%@",HOST, NAMESPACE,URL_SEND_CHAT];
chatsNotSent = [parameters objectAtIndex:0];
NSString *response;
NSMutableDictionary *params = [[NSMutableDictionary alloc] init];
//...
NSError *error;
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:[ChatJSONParser wrapChatArray: chatsNotSent] options:0 error:&error];
NSString *JSONString = [[NSString alloc] initWithBytes:[jsonData bytes] length:[jsonData length] encoding:NSUTF8StringEncoding];
[params setObject:JSONString forKey:@"chats"];
response = [HTTPClient executePOST:sendChatsURL parameters:params];
if([respuesta isEqualToString:@"true"]){
return 1;
}else{
return -1;
}
}
- (void) postExecute: (NSInteger) result{
//Method to override
if (result == 1) {
for (Chat *chat in chatsNotSent) {
chat.state = STATE_NOT_SENT;
[chat save];
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
[appDelegate refreshChat];
}
} else {
}
}
@end
И следующий вызов:
[[[SendChatTask alloc] init] executeParameters:[NSArray arrayWithObjects: chatsNotSent, nil]];
Вы можете добавить метод обновления publishProgress()
и соответствующий... Я не использую его на данный момент, потому что я вызываю свою асинхронную задачу в фоновом режиме.
Я надеюсь, что это будет полезно.
Ответ 3
если ваша таргетинг на более раннюю версию iOS (чем iOS 4 для Grand Central Dispatch), вы можете использовать методы NSObject performSelector
-
Выполнение в фоновом режиме Тема выполнитьSelectorInBackground: withObject:
-
И выполнить на MainThread выполнитьSelectorOnMainThread: withObject: waitUntilDone:
Это пример:
[self performSelectorInBackground:@selector(executeInBackground) withObject:nil];
-(void) executeInBackground
{
NSLog(@"executeInBackground");
[self performSelectorOnMainThread:@selector(executeOnMainThread) withObject:nil waitUntilDone:NO];
}
-(void) executeOnMainThread
{
NSLog(@"executeOnMainThread");
}
Ответ 4
Swift 3
В Android, когда я хотел запустить задачу в фоновом потоке, а затем обновил интерфейс после его завершения, я использовал AsyncTask
(example). Теперь, когда я делаю iOS-версии своих приложений, я использую Grand Central Dispatch (GCD), чтобы сделать то же самое. Вот как это делается с помощью Swift:
DispatchQueue.global(qos: .background).async {
// code to be run on a background task
DispatchQueue.main.async {
// code to be run on the main thread after the background task is finished
}
}