ios – 为什么NSOperationQueue在主线程上处理大量任务时比GCD或performSelectorOnMainThread更快?

ios – 为什么NSOperationQueue在主线程上处理大量任务时比GCD或performSelectorOnMainThread更快?,第1张

概述例如,我有100次for循环.并且需要更新UI ImageView,最后2个方法慢慢相同.为什么?他们之间有什么不同? //fastest [[NSOperationQueue mainQueue] addOperationWithBlock:^{ [btnThumb setImage:[UIImage imageWithData:data] 例如,我有100次for循环.并且需要更新UI ImageVIEw,最后2个方法慢慢相同.为什么?他们之间有什么不同?

//fastest    [[NSOperationQueue mainQueue] addOperationWithBlock:^{                       [btnThumb setimage:[UIImage imageWithData:data] forState:UIControlStatenormal];                        [scrollVIEw addSubvIEw:btnThumb];                   }];//slowly                     dispatch_async(dispatch_get_main_queue(),^                    {                        [btnThumb setimage:[UIImage imageWithData:data] forState:UIControlStatenormal];                        [scrollVIEw addSubvIEw:btnThumb];                    });       //slowly                   [btnThumb setimage:[UIImage imageWithData:data] forState:UIControlStatenormal];                   [self performSelectorOnMainThread:@selector(testMethod:) withObject:[NSArray arrayWithObjects:scrollVIEw,btnThumb,nil] waitUntilDone:NO];    -(voID) testMethod:(NSArray*)obJs    {        UIScrollVIEw *scroll = [obJs objectAtIndex:0];        UIbutton *btn = [obJs lastObject];        [scroll addSubvIEw:btn];    }
解决方法 对于后代,为了避免对此有任何疑问,让我们考虑以下测试工具,内置在一个简单的空应用程序模板中.它使用每种机制执行1000次 *** 作,并且还有一个运行循环观察器,因此我们可以看到我们的排队异步任务如何与主运行循环的旋转相关.它记录到控制台,但是异步执行,因此NSLog的成本不会混淆我们的测量.它还故意阻塞主线程,同时它将NSOperations / dispatch_asyncs / performSelectors任务排入队列,因此排队行为也不会干扰.这是代码:

#import "NSAppDelegate.h"dispatch_queue_t gLogQueue;#define NSLogAsync(...) dispatch_async(gLogQueue,^{ NSLog(__VA_ARGS__); });@implementation NSAppDelegate{    dispatch_group_t g;    NSUInteger numOps;    useconds_t usleepDuration;}static voID MyCFRunLoopObserverCallBack(CFRunLoopObserverRef observer,CFRunLoopActivity activity,voID *info);- (voID)applicationDIDFinishLaunching:(NSNotification *)aNotification{    // parameters of test    numOps = 1000;    usleepDuration = 1000;    // Set up a serial queue so we can keep the cost of calling NSLog more or less out of our test case.    gLogQueue = dispatch_queue_create(NulL,disPATCH_QUEUE_SERIAL);    // Group allows us to wait for one test to finish before the next one begins    g = dispatch_group_create();    // Insert code here to initialize your application    CFRunLoopObserverRef rlo = CFRunLoopObserverCreate(NulL,kcfRunLoopAllActivitIEs,YES,MyCFRunLoopObserverCallBack,NulL);    CFRunLoopAddobserver(CFRunLoopGetCurrent(),rlo,kcfRunLoopCommonModes);    CFRelease(rlo);    NSCondition* cond = [[NSCondition alloc] init];    dispatch_async(dispatch_get_global_queue(0,0),^{        NSTimeInterval start = 0,end = 0;        // pause the main thread        dispatch_async(dispatch_get_main_queue(),^{            [cond lock];            [cond signal];            [cond wait];            [cond unlock];        });        // wait for the main thread to be paused        [cond lock];        [cond wait];        // NSOperationQueue        for (NSUInteger i = 0; i < numOps; ++i)        {            dispatch_group_enter(g);            [[NSOperationQueue mainQueue] addOperationWithBlock:^{                NSLogAsync(@"NSOpQ task #%@",@(i));                usleep(usleepDuration); // simulate work                dispatch_group_leave(g);            }];        }        // unpause the main thread        [cond signal];        [cond unlock];        // mark start time        start = [NSDate timeIntervalSinceReferenceDate];        // wait for it to be done        dispatch_group_wait(g,disPATCH_TIME_FOREVER);        end = [NSDate timeIntervalSinceReferenceDate];        NSTimeInterval opQDuration = end - start;        NSLogAsync(@"NSOpQ took: %@s",@(opQDuration));        // pause the main thread        dispatch_async(dispatch_get_main_queue(),^{            [cond lock];            [cond signal];            [cond wait];            [cond unlock];        });        // wait for the main thread to be paused        [cond lock];        [cond wait];        // dispatch_async        for (NSUInteger i = 0; i < numOps; ++i)        {            dispatch_group_enter(g);            dispatch_async(dispatch_get_main_queue(),^{                NSLogAsync(@"dispatch_async main thread task #%@",@(i));                usleep(usleepDuration); // simulate work                dispatch_group_leave(g);            });        }        // unpause the main thread        [cond signal];        [cond unlock];        // mark start        start = [NSDate timeIntervalSinceReferenceDate];        dispatch_group_wait(g,disPATCH_TIME_FOREVER);        end = [NSDate timeIntervalSinceReferenceDate];        NSTimeInterval asyncDuration = end - start;        NSLogAsync(@"dispatch_async took: %@s",@(asyncDuration));        // pause the main thread        dispatch_async(dispatch_get_main_queue(),^{            [cond lock];            [cond signal];            [cond wait];            [cond unlock];        });        // wait for the main thread to be paused        [cond lock];        [cond wait];        // performSelector:        for (NSUInteger i = 0; i < numOps; ++i)        {            dispatch_group_enter(g);            [self performSelectorOnMainThread: @selector(selectortoperftask:) withObject: @(i) waitUntilDone: NO];        }        // unpause the main thread        [cond signal];        [cond unlock];        // mark start        start = [NSDate timeIntervalSinceReferenceDate];        dispatch_group_wait(g,disPATCH_TIME_FOREVER);        end = [NSDate timeIntervalSinceReferenceDate];        NSTimeInterval performDuration = end - start;        NSLogAsync(@"performSelector took: %@s",@(performDuration));        // Done.        dispatch_async(dispatch_get_main_queue(),^{            CFRunLoopRemoveObserver(CFRunLoopGetCurrent(),kcfRunLoopCommonModes);            NSLogAsync(@"Done. NSOperationQueue: %@s dispatch_async: %@s performSelector: %@",@(opQDuration),@(asyncDuration),@(performDuration));        });    });}- (voID)selectortoperftask: (NSNumber*)task{    NSLogAsync(@"performSelector task #%@",task);    usleep(usleepDuration); // simulate work    dispatch_group_leave(g);}static Nsstring* NsstringFromCFRunLoopActivity(CFRunLoopActivity activity){    Nsstring* foo = nil;    switch (activity) {        case kcfRunLoopEntry:            foo = @"kcfRunLoopEntry";            break;        case kcfRunLoopBeforeTimers:            foo = @"kcfRunLoopBeforeTimers";            break;        case kcfRunLoopBeforeSources:            foo = @"kcfRunLoopBeforeSources";            break;        case kcfRunLoopBeforeWaiting:            foo = @"kcfRunLoopBeforeWaiting";            break;        case kcfRunLoopAfterWaiting:            foo = @"kcfRunLoopAfterWaiting";            break;        case kcfRunLoopExit:            foo = @"kcfRunLoopExit";            break;        default:            foo = @"ERROR";            break;    }    return foo;}static voID MyCFRunLoopObserverCallBack(CFRunLoopObserverRef observer,voID *info){    NSLogAsync(@"RLO: %@",NsstringFromCFRunLoopActivity(activity));}@end

在此代码的输出中,我们看到以下内容(删除了不相关/重复部分):

RLO: kcfRunLoopEntryRLO: kcfRunLoopBeforeTimersRLO: kcfRunLoopBeforeSourcesRLO: kcfRunLoopBeforeWaitingRLO: kcfRunLoopAfterWaitingNSOpQ task #0RLO: kcfRunLoopExitRLO: kcfRunLoopEntryRLO: kcfRunLoopBeforeTimersRLO: kcfRunLoopBeforeSourcesRLO: kcfRunLoopBeforeWaitingRLO: kcfRunLoopAfterWaitingNSOpQ task #1RLO: kcfRunLoopExit... pattern repeats ...RLO: kcfRunLoopEntryRLO: kcfRunLoopBeforeTimersRLO: kcfRunLoopBeforeSourcesRLO: kcfRunLoopBeforeWaitingRLO: kcfRunLoopAfterWaitingNSOpQ task #999RLO: kcfRunLoopExitNSOpQ took: 1.237247049808502sRLO: kcfRunLoopEntryRLO: kcfRunLoopBeforeTimersRLO: kcfRunLoopBeforeSourcesRLO: kcfRunLoopBeforeWaitingRLO: kcfRunLoopAfterWaitingdispatch_async main thread task #0dispatch_async main thread task #1... pattern repeats ...dispatch_async main thread task #999dispatch_async took: 1.118762016296387sRLO: kcfRunLoopExitRLO: kcfRunLoopEntryRLO: kcfRunLoopBeforeTimersRLO: kcfRunLoopBeforeSourcesperformSelector task #0performSelector task #1... pattern repeats ...performSelector task #999performSelector took: 1.133482992649078sRLO: kcfRunLoopExitRLO: kcfRunLoopEntryRLO: kcfRunLoopBeforeTimersRLO: kcfRunLoopBeforeSourcesDone. NSOperationQueue: 1.237247049808502s dispatch_async: 1.118762016296387s performSelector: 1.133482992649078

这告诉我们的是,在主队列上排队的NSOperations每次运行循环执行一次. (顺便说一句,这将允许为每个 *** 作绘制视图,因此如果你在OP中更新这些任务中的UI控件,这将允许它们绘制.)使用dispatch_async(dispatch_get_main_queue(),… )和 – [performSelectorOnMainThread:…]所有排队的块/选择器被一个接一个地调用,而不会让视图绘制或类似的东西. (如果你在排队任务时没有强行暂停主runloop,你有时可以在排队过程中看到运行循环旋转一次或两次.)

最后,结果是我预期的结果:

> NSOperationQueue:1.2372s
> dispatch_async:1.1188s
> performSelector:1.1335s

NSOperationQueue总是会变慢,因为旋转运行循环不是免费的.在这个测试工具中,运行循环甚至没有做任何实质内容,并且它已经比dispatch_async慢10%.如果它正在做任何实质内容,比如重新绘制视图,那就会慢得多.至于dispatch_async和performSelectorOnMainThread:两者都在运行循环的一次旋转中执行所有入队项,因此差异非常小.我希望它可以归结为消息发送开销和管理目标的retain / releases以及performSelector的参数….

因此,与问题的含义相反,NSOperationQueue客观上不是三种机制中最快的,而是最慢的.我怀疑在OP的情况下,NSOperationQueue看起来更快,因为它的“第一次可见变化的时间”会更短,而对于dispatch_async和performSelector,所有入队的 *** 作都将被执行,然后才会重新绘制并显示新的国家.在病态情况下,我希望这意味着只有最后一帧被看到,虽然如果你在排队时没有阻止主线程,你可以得到一些可见的帧(但你实际上会有所下降)在地面上的框架.)

无论哪种异步执行机制客观上最快,它们都是非常糟糕的动画制作方式.

总结

以上是内存溢出为你收集整理的ios – 为什么NSOperationQueue在主线程上处理大量任务时比GCD或performSelectorOnMainThread更快?全部内容,希望文章能够帮你解决ios – 为什么NSOperationQueue在主线程上处理大量任务时比GCD或performSelectorOnMainThread更快?所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址:https://54852.com/web/1076227.html

(0)
打赏 微信扫一扫微信扫一扫 支付宝扫一扫支付宝扫一扫
上一篇 2022-05-26
下一篇2022-05-26

发表评论

登录后才能评论

评论列表(0条)

    保存