iOS / Objective-C相当于Android的AsyncTask


Answers:


102

原始问题的答案:

大型中央调度(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的很好的答案,您可以看到文档描述了与Objective-C对象等效的GCD队列:

* 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的分组接口支持同步多个异步块。乔恩Eyrich和ɲeuroburɳ提供这个话题的一个慷慨的解释在这里。如果您需要此功能,我强烈建议您花几分钟时间仔细阅读它们的两个答案并了解它们之间的区别。

如果您愿意的话,文档中包含有关该主题的大量信息。


3
有趣。是否在主线程上执行了回调?
2012年

并不queue需要与被破坏dispatch_release
尼克,

从iOS 6.0(和OS X 10.8)开始,ARC将管理Grand Central Dispatch对象,因为它们被视为Objective-C对象。因此,如果您的部署目标是<iOS 6.0或OS X 10.8,则queue必须明确发布。否则,否,ARC将处理它。
eric.mitchell 2014年

18

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()更新方法和相应的方法...目前暂时不使用它,因为我在后台服务中调用了异步任务。

希望对您有所帮助。


可能更具描述性...复制/粘贴的
弊端

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
不到5%的iOS用户<4.0
eric.mitchell 2012年

1
我完全同意,针对低iOS版本进行优化可能并不总是值得的。但是我更喜欢performSelector的ObjectiveC-API,而不是Grand Central Dispatch C中的API。iOS 2+部分只是一个额外功能。
JeanLuc

1
是的,我明白。我实际上更喜欢C样式的语法,因为您不需要为每个任务创建一个新方法,并且共享变量更容易(NSObject方法只能使用一个参数,并且传递一组已分类的对象很麻烦)
eric .mitchell 2012年

@JeanLuc此解决方案与@Rickay的解决方案之间的性能含义是什么?
2012年

老实说我不能说。在运行时,它们可能做几乎相同的事情,但是语法只是不同。我真的不知道 stackoverflow.com/questions/5653522/…可能会给您一些信息
eric.mitchell 2012年

1

迅捷3

在Android中,当我想在后台线程上运行任务,然后在完成时更新UI时,我使用了AsyncTask示例)。现在,当我制作应用程序的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

这是带有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);
}

并实现:

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

     }
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.