iOS/Objective-C中与Android的AsyncTask相当的内容

70

我熟悉在Android中使用 AsyncTask:创建一个子类,对子类的一个实例调用 execute 方法,onPostExecute 会在UI线程或主线程上被调用。那么在iOS中有相当的功能吗?

6个回答

103

原问题的答案:

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
});

其他要点:

1) 回调函数

如果你想在后台执行任务并在后台任务完成时更新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) 全局队列

创建队列时,你也可以使用dispatch_get_global_queue()函数获取具有特定优先级(如DISPATCH_QUEUE_PRIORITY_HIGH)的全局调度队列。这些队列是普遍可访问的,并且在你想要将多个任务分配到同一个线程/队列时非常有用。注意,iOS完全为你管理内存。

3) 内存

有时候关于内存管理和调度队列会存在一些困惑,因为它们拥有自己的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) 多任务/多块

我想补充一点,GCD提供了一个分组接口,如果一个任务需要等待多个异步操作完成后才能继续执行,它就支持同步多个异步块。Jörn Eyrich和ɲeuroburɳ在这个主题上提供了很详细的解释here。如果您需要这种功能,我强烈建议您花几分钟仔细阅读他们两个答案并理解它们之间的差异。

文档中有丰富的关于这个主题的信息,如果您感兴趣可以去了解一下。


3
有趣。是否有一个在主线程上执行的回调函数? - SundayMonday
queue 需要使用 dispatch_release 进行销毁吗? - Nick
从iOS 6.0(和OS X 10.8)开始,ARC将管理Grand Central Dispatch对象,因为它们被视为Objective-C对象。因此,如果您的部署目标小于iOS 6.0或OS X 10.8,则需要显式释放“queue”。否则,ARC将处理它。 - eric.mitchell

17

iOS中没有针对此的类,但您可以使用队列进行模拟。 您可以调用:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    //Your code to execute in background...
});

对于异步任务,在您的异步代码中调用下一个队列以在视图中执行某些操作...

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()更新方法和相应的...我现在不使用它,因为我在后台服务中调用我的异步任务。
我希望这对你有所帮助。

可能比必要的描述更详细...复制/粘贴的坏处 - IgniteCoders

5
如果您的目标是早期的iOS版本(比Grand Central Dispatch更早的iOS 4),可以使用NSObject performSelector方法。 这是一个例子:
[self performSelectorInBackground:@selector(executeInBackground) withObject:nil];


-(void) executeInBackground
{
    NSLog(@"executeInBackground");

    [self performSelectorOnMainThread:@selector(executeOnMainThread) withObject:nil waitUntilDone:NO];
}

-(void) executeOnMainThread
{
    NSLog(@"executeOnMainThread");
}

6
iOS用户中少于5%的人使用4.0以下版本。 - eric.mitchell
1
我完全同意,为低版本的iOS进行优化可能并不总是值得的。 但是我更喜欢使用performSelector的Objective-C API,而不是Grand Central Dispatch的C语言API。iOS 2+部分只是一个额外的功能。 - JeanLuc
1
是的,我理解。实际上,我更喜欢 C 风格的语法,因为你不需要为每个任务创建一个新方法,共享变量也更容易(NSObject 方法只能接受一个参数,传递杂乱的对象数组很麻烦)。 - eric.mitchell
@JeanLuc,这种解决方案与@Rickay的解决方案之间的性能影响如何? - SundayMonday
老实说我不知道。在运行时,它们可能做的事情基本相同,但语法只是不同而已。我真的不知道。https://dev59.com/Fm035IYBdhLWcg3wC7mf 可能会给你一些信息。 - eric.mitchell
显示剩余2条评论

1

Swift 3

在 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
    }
}

笔记


0

最简单的方法是使用GCD[关于]作为AsyncTask[关于]的一种变体

DispatchQueue.global().async {
    //background thread
    
    DispatchQueue.main.async {
        //main thread
    }
}

0

这是一个带有PusblishProgress的C# 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);
}

实现:

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

     }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接