iOS/Objective-C 等效于 Android 的 AsyncTask

Posted

技术标签:

【中文标题】iOS/Objective-C 等效于 Android 的 AsyncTask【英文标题】:iOS/Objective-C equivalent of Android's AsyncTask 【发布时间】:2012-07-28 12:15:08 【问题描述】:

我熟悉在 android 中使用 AsyncTask:创建一个子类,在子类的实例上调用 execute,在 UI 线程或主线程上调用 onPostExecuteiOS 中的等价物是什么?

【问题讨论】:

【参考方案1】:

原始问题的答案:

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's great answer 关于 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。如果您需要此功能,我强烈建议您花几分钟时间仔细阅读他们的两个答案并了解它们之间的区别。

documentation 有大量关于该主题的信息,如果您愿意的话。

【讨论】:

有趣。是否有在主线程上执行的回调? queue不需要用dispatch_release销毁吗? 从 iOS 6.0(和 OS X 10.8)开始,ARC 将管理 Grand Central Dispatch 对象,因为它们被视为 Objective-C 对象。因此,如果您的部署目标是 queue 必须明确发布。否则,不,ARC 会处理它。【参考方案2】:

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

希望对你有帮助。

【讨论】:

可能比必要的更具描述性......复制/粘贴的坏处【参考方案3】:

如果您的目标是更早的 iOS 版本(Grand Central Dispatch 比 iOS 4),您可以使用 NSObject performSelector 方法

在后台执行线程performSelectorInBackground:withObject:

并且在主线程上执行 performSelectorOnMainThread:withObject:waitUntilDone:

这是一个例子:

[self performSelectorInBackground:@selector(executeInBackground) withObject:nil];


-(void) executeInBackground

    NSLog(@"executeInBackground");

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


-(void) executeOnMainThread

    NSLog(@"executeOnMainThread");

【讨论】:

不到 5% 的 iOS 用户是 我完全同意,为低 iOS 版本优化可能并不总是值得的。但我更喜欢performSelector的ObjectiveC-API而不是Grand Central Dispatch的C中的API。 iOS 2+ 部分只是一个附加功能。 是的,我明白了。我其实更喜欢 C 风格的语法,因为你不需要为每个任务创建一个新方法,并且共享变量更容易(NSObject 方法只能接受一个参数,传递一个分类对象数组很麻烦)跨度> @JeanLuc 此解决方案与@Rickay 的解决方案之间的性能影响是什么? 老实说我不能说。有可能在运行时,它们都做几乎相同的事情,但语法不同。我真的不知道。 ***.com/questions/5653522/… 可能会给你一些信息【参考方案4】:

斯威夫特 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
    

注意事项

Grand Central Dispatch Tutorial for Swift

【讨论】:

【参考方案5】:

这是一个带有 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

     

【讨论】:

以上是关于iOS/Objective-C 等效于 Android 的 AsyncTask的主要内容,如果未能解决你的问题,请参考以下文章

在 OkHttp 中等效的 setEntity - Android

持久登录 iOS Objective-C

只要 UIView 可见,我如何使 UIView 出现在我的所有应用程序屏幕上 iOS Objective-c

IOS/Objective-C:用于识别命名实体的 NSLinguisticTagger

IOS / Objective-C:测试图像是不是存在[重复]

C# 等效于 Java 标点正则表达式