iOS/Objective-C 相当于 Android 的 AsyncTask
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/11728098/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
iOS/Objective-C equivalent of Android's AsyncTask
提问by SundayMonday
I'm familiar with using AsyncTask
in Android: create a subclass, call execute
on an instance of the subclass and onPostExecute
is called on the UI thread or main thread. What's the equivalent in iOS?
我熟悉AsyncTask
在Android中使用:创建一个子类,调用子类execute
的实例并onPostExecute
在UI线程或主线程上调用。iOS 中的等价物是什么?
回答by eric.mitchell
Answer to Original Question:
对原始问题的回答:
Grand Central Dispatch (GCD) offers a mechanism to perform tasks in the background, though it works in a structurally different way than AsyncTask. To perform something asynchronously, you just need to create a queue (like a thread) and then pass a block to dispatch_async()
to be performed in the background. I find it neater than AsyncTask, as there is no subclassing involved; it is more or less plug-and-play wherever you have code you'd like to execute in the background. An example:
Grand Central Dispatch (GCD) 提供了一种在后台执行任务的机制,尽管它在结构上与 AsyncTask 不同。要异步执行某事,您只需要创建一个队列(如线程),然后传递一个dispatch_async()
要在后台执行的块。我发现它比 AsyncTask 更简洁,因为不涉及子类化;无论您有想要在后台执行的代码,它都或多或少是即插即用的。一个例子:
dispatch_queue_t queue = dispatch_queue_create("com.yourdomain.yourappname", NULL);
dispatch_async(queue, ^{
//code to be executed in the background
});
Other Points:
其他要点:
1) Callbacks
1) 回调
If you want to perform a task in the background and update the UI (or do something on another thread) when the background task is done, you can simply nest the dispatch calls:
如果您想在后台执行任务并在后台任务完成时更新 UI(或在另一个线程上执行某些操作),您可以简单地嵌套调度调用:
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) Global Queues
2) 全局队列
When creating a queue, you can also use the dispatch_get_global_queue()
function to get a global dispatch queue with a certain priority (such as DISPATCH_QUEUE_PRIORITY_HIGH
). These queues are universally accessible and are useful when you want to assign multiple tasks to the same thread/queue. Note that memory is managed for you completely by iOS.
创建队列时,也可以使用该dispatch_get_global_queue()
函数获取具有一定优先级(如DISPATCH_QUEUE_PRIORITY_HIGH
)的全局调度队列。这些队列是普遍可访问的,当您想将多个任务分配给同一个线程/队列时非常有用。请注意,内存完全由 iOS 为您管理。
3) Memory
3) 内存
There is sometimes some confusion regarding memory management and dispatch queues because they have their own dispatch_retain
/dispatch_release
functions. However, rest assured that they are treated as Objective-C objects by ARC, so you don't need to worry about calling these functions. Referencing rob mayoff's great answerregarding GCD and ARC, you can see the documentation describe GCD queues' equivalence with Objective-C objects:
关于内存管理和调度队列有时会有些混乱,因为它们有自己的dispatch_retain
/dispatch_release
功能。但是,请放心,它们被 ARC 视为 Objective-C 对象,因此您无需担心调用这些函数。参考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) Multiple Tasks/Blocks
4) 多个任务/块
I'll add that GCD has a grouping interface supports synchronizing multiple asynchronous blocks if a task cannot continue until multiple asynchronous activities have completed. J?rn Eyrich and ?eurobur? provide a generous explanation of this topic here. If you need this functionality, I would highly recommend taking a few minutes to read both of their answers closely and understand the differences between them.
我要补充一点,如果任务在多个异步活动完成之前无法继续,GCD 有一个分组接口支持同步多个异步块。J?rn Eyrich 和?eurobur? 请在此处对该主题进行详尽的解释。如果您需要此功能,我强烈建议您花几分钟仔细阅读他们的答案并了解它们之间的差异。
The documentationhas a wealth of information on the topic if you are so inclined.
如果您愿意,文档中有大量关于该主题的信息。
回答by IgniteCoders
There are no classes for that in iOS but you can simulate it using queues. You can call:
iOS 中没有与此相关的类,但您可以使用队列来模拟它。您可以致电:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//Your code to execute in background...
});
for async tasks and inside your async code call next queue to do something in the view...:
对于异步任务,在异步代码中调用 next 队列在视图中执行某些操作...:
dispatch_async(dispatch_get_main_queue(), ^{
//Your code to execute on UIthread (main thread)
});
Then, using this two queues you can create a asyncTask class, add this class to your project to implement them:
然后,使用这两个队列,您可以创建一个 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
This is an example that I am using in a project:
这是我在项目中使用的示例:
#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
And the following call:
以及以下调用:
[[[SendChatTask alloc] init] executeParameters:[NSArray arrayWithObjects: chatsNotSent, nil]];
You can add a publishProgress()
update method and respective... I don't use it for the moment because I call my async task in background services.
您可以添加publishProgress()
更新方法和相应的...我暂时不使用它,因为我在后台服务中调用我的异步任务。
I hope it's helpful.
我希望它有帮助。
回答by JeanLuc
if your targeting earlier iOS Version (than iOS 4 for Grand Central Dispatch) you could use the NSObject performSelector methods
如果你的目标是更早的 iOS 版本(比 iOS 4 的 Grand Central Dispatch)你可以使用 NSObject performSelector 方法
Execute on BackgroundThread performSelectorInBackground:withObject:
And execute on MainThreadperformSelectorOnMainThread:withObject:waitUntilDone:
并在 MainThread 上执行performSelectorOnMainThread:withObject:waitUntilDone:
This is an example:
这是一个例子:
[self performSelectorInBackground:@selector(executeInBackground) withObject:nil];
-(void) executeInBackground
{
NSLog(@"executeInBackground");
[self performSelectorOnMainThread:@selector(executeOnMainThread) withObject:nil waitUntilDone:NO];
}
-(void) executeOnMainThread
{
NSLog(@"executeOnMainThread");
}
回答by Suragch
Swift 3
斯威夫特 3
In Android when I wanted to run a task on a background thread and then update the UI when it finished, I used AsyncTask
(example). Now when I am making iOS versions of my apps, I use Grand Central Dispatch(GCD) to do the same thing. Here is how it is done with Swift:
在 Android 中,当我想在后台线程上运行任务然后在完成后更新 UI 时,我使用了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
}
}
Notes
笔记
回答by Olivier MATROT
Here is a c# Xamarin.iOS version with PusblishProgress:
这是带有 PusblishProgress 的 ac# Xamarin.iOS 版本:
internal abstract class AsyncTask : NSObject
{
protected abstract nint DoInBackground(NSArray parameters);
protected abstract void PostExecute(nint result);
public void ExecuteParameters(NSArray @params)
{
this.PreExecute();
DispatchQueue.GetGlobalQueue(DispatchQueuePriority.Default).DispatchAsync(() =>
{
//We're on a Background thread
var result = this.DoInBackground(@params);
DispatchQueue.MainQueue.DispatchAsync(() => {
// We're on the main thread
this.PostExecute(result);
});
});
}
protected abstract void PreExecute();
protected void PublishProgress(NSArray parameters)
{
InvokeOnMainThread(() => {
// We're on the main thread
this.OnProgressUpdate(parameters);
});
}
protected abstract void OnProgressUpdate(NSArray parameters);
}
And implementation:
和实施:
internal class MyAsyncTask : AsyncTask
{
protected override void OnProgressUpdate(NSArray parameters)
{
// This runs on the UI Thread
}
protected override nint DoInBackground(NSArray parameters)
{
// Do some background work
// ....
var progress = NSArray.FromObjects(1, "Done step 1");
PublishProgress(progress);
return 0;
}
protected override void PostExecute(nint result)
{
// This runs on the UI Thread
}
protected override void PreExecute()
{
// This runs on the UI Thread
}
}