iOS - 后台进程和 UI 更新

发布于 2024-11-28 05:43:45 字数 2125 浏览 2 评论 0原文

问题很简单:我的应用程序控制每次启动时是否有更新。如果有更新,将显示一个弹出窗口,并选择“是”或“否”。当用户点击“是”时,将启动 4 种方法。这些方法下载 xml 文件并上传 CoreData。这是警报的代码:

-(void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {

    if (buttonIndex==1) {
        [self showActivityViewer];
        [self downloadControlAndUpdatePoi];
        [self downloadControlAndUpdateItinerari];
        [self downloadControlAndUpdateArtisti];
        [self downloadControlAndUpdateEventi];
        [self hideActivityViewer];
        NSLog(@"AGGIORNA");
    } else {
        NSLog(@"NON AGGIORNARE");
        return;
    }
}

但是有一个问题:当用户点击“是”时,警报不会消失并保留在屏幕上,直到所有方法完成。所以我尝试这个其他代码:

-(void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {

    if (buttonIndex==1) {
        [self showActivityViewer];
        [NSThread detachNewThreadSelector:@selector(startDownloads) toTarget:self withObject:nil];
        [self hideActivityViewer];
        NSLog(@"AGGIORNA");
    } else {
        NSLog(@"NON AGGIORNARE");
        return;
    }
}

-(void)startDownloads {
    NSInvocationOperation *opPoi=[[NSInvocationOperation alloc] initWithTarget:self selector:@selector(downloadControlAndUpdatePoi) object:nil];
    NSInvocationOperation *opItinerari=[[NSInvocationOperation alloc] initWithTarget:self selector:@selector(downloadControlAndUpdateItinerari) object:nil];
    NSInvocationOperation *opArtisti=[[NSInvocationOperation alloc] initWithTarget:self selector:@selector(downloadControlAndUpdateArtisti) object:nil];
    NSInvocationOperation *opEventi=[[NSInvocationOperation alloc] initWithTarget:self selector:@selector(downloadControlAndUpdateEventi) object:nil];
    NSArray *operations=[[NSArray alloc] initWithObjects:opPoi,opItinerari,opArtisti,opEventi, nil];
    NSOperationQueue *queue=[[NSOperationQueue alloc] init];
    [queue addOperations:operations waitUntilFinished:YES];
    [queue waitUntilAllOperationsAreFinished];

}

即使在这里也存在问题:我点击开始,但活动查看器没有出现。警报消失,线程启动并依次运行4个方法。

我需要在后台运行进程,就像我的第二个代码一样,但我什至需要运行我的 showActityViewer 方法并显示微调器。

谢谢 :)

the question is simple : my app control if there is an update every time it starts. If there is an update a popup will be shown with a Yes or No choose. When user tap Yes 4 methods start. These methods download xml file and upload CoreData. This is the code of the alert :

-(void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {

    if (buttonIndex==1) {
        [self showActivityViewer];
        [self downloadControlAndUpdatePoi];
        [self downloadControlAndUpdateItinerari];
        [self downloadControlAndUpdateArtisti];
        [self downloadControlAndUpdateEventi];
        [self hideActivityViewer];
        NSLog(@"AGGIORNA");
    } else {
        NSLog(@"NON AGGIORNARE");
        return;
    }
}

But there is a problem : when user tap Yes the alert doesn't disappear and remain on screen until all methods are finished. So i try this other code :

-(void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {

    if (buttonIndex==1) {
        [self showActivityViewer];
        [NSThread detachNewThreadSelector:@selector(startDownloads) toTarget:self withObject:nil];
        [self hideActivityViewer];
        NSLog(@"AGGIORNA");
    } else {
        NSLog(@"NON AGGIORNARE");
        return;
    }
}

-(void)startDownloads {
    NSInvocationOperation *opPoi=[[NSInvocationOperation alloc] initWithTarget:self selector:@selector(downloadControlAndUpdatePoi) object:nil];
    NSInvocationOperation *opItinerari=[[NSInvocationOperation alloc] initWithTarget:self selector:@selector(downloadControlAndUpdateItinerari) object:nil];
    NSInvocationOperation *opArtisti=[[NSInvocationOperation alloc] initWithTarget:self selector:@selector(downloadControlAndUpdateArtisti) object:nil];
    NSInvocationOperation *opEventi=[[NSInvocationOperation alloc] initWithTarget:self selector:@selector(downloadControlAndUpdateEventi) object:nil];
    NSArray *operations=[[NSArray alloc] initWithObjects:opPoi,opItinerari,opArtisti,opEventi, nil];
    NSOperationQueue *queue=[[NSOperationQueue alloc] init];
    [queue addOperations:operations waitUntilFinished:YES];
    [queue waitUntilAllOperationsAreFinished];

}

There is a problem even here : i tap start, but the activity viewer doesn't appear. The alert disappear and the thread start and run the 4 methods one after another.

I need the processes run in background, just like happened with my 2nd code, but i need even my showActityViewer method will be run and show the spinner.

Thanks :)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

美胚控场 2024-12-05 05:43:45

首先要事。您不需要启动 4 个操作,因为您已经处于辅助线程中,并且不需要并行执行这 4 个操作。你可以简单地这样做:

-(void)startDownloads {
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];  
    [self downloadControlAndUpdatePoi];
    [self downloadControlAndUpdateItinerari];
    [self downloadControlAndUpdateArtisti];
    [self downloadControlAndUpdateEventi];
    [pool release];
}

最重要的是,如果你在 downloadControl* 方法中使用 autorelease ,你需要在 startDownloads 中定义一个自动释放池,否则我怀疑你将会有泄漏。

至于为什么活动指示器不显示,这取决于您正在调用的事实:

    [self hideActivityViewer];

在分离之后立即调用。因此,在 UI 有时间更新自身之前,您将显示并删除它。从那里删除该行并重写 startDownloads 如下:

-(void)startDownloads {
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];  
    [self downloadControlAndUpdatePoi];
    [self downloadControlAndUpdateItinerari];
    [self downloadControlAndUpdateArtisti];
    [self downloadControlAndUpdateEventi];
    [self performSelectorOnMainThread:@selector(hideActivityViewer) withObject:nil waitUntilDone:NO];  

    [pool release];
}

请注意,我正在调用主线程来 hideActivityViewer 因为只有主线程可以安全地使用 UIKit< /代码>。
编辑:

我不知道您在下载方法中使用了 Core Data...

看看 与核心数据的并发。您将需要通过至少为辅助线程使用单独的托管对象上下文来稍微调整您的代码(我不知道您在那里创建 moc 是否可行)。

另请参阅此教程可可是我的女朋友

作为所有这些的替代方案,您可以考虑这样做:

if (buttonIndex==1) {
    [self showActivityViewer];
    [self performSelector:@selector(startDownloads) withObject:nil afterDelay:0];
    NSLog(@"AGGIORNA");
} else {
    NSLog(@"NON AGGIORNARE");
    return;
}

with:

-(void)startDownloads {
    [self downloadControlAndUpdatePoi];
    [self downloadControlAndUpdateItinerari];
    [self downloadControlAndUpdateArtisti];
    [self downloadControlAndUpdateEventi];
    [self hideActivityViewer];  
}

这根本不使用线程,但我不确定活动查看器是否会显示没有任何故障。如果需要的话,可以进行更多级别的黑客攻击,并且您可以指定延迟

[self performSelector:@selector(startDownloads) withObject:nil afterDelay:0.1];

First things first. You don't need to start 4 operations, since you are already in a secondary thread and do not need that the 4 operations be executed in parallel. You could simply do:

-(void)startDownloads {
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];  
    [self downloadControlAndUpdatePoi];
    [self downloadControlAndUpdateItinerari];
    [self downloadControlAndUpdateArtisti];
    [self downloadControlAndUpdateEventi];
    [pool release];
}

Above all, you need define an autorelease pool in startDownloads if you use autorelease in the downloadControl* methods, otherwise I suspect you will have leaks.

As to why the activity indicator does not show up, it depends on the fact that you are calling:

    [self hideActivityViewer];

immediately after detach. So, you are showing it and removing it, before event the UI has got the time to update itself. Remove that line from there and rewrite startDownloads like this:

-(void)startDownloads {
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];  
    [self downloadControlAndUpdatePoi];
    [self downloadControlAndUpdateItinerari];
    [self downloadControlAndUpdateArtisti];
    [self downloadControlAndUpdateEventi];
    [self performSelectorOnMainThread:@selector(hideActivityViewer) withObject:nil waitUntilDone:NO];  

    [pool release];
}

Here notice that I am calling into the main thread to hideActivityViewer because only the main thread can safely use UIKit.
EDIT:

I did not know that you were using Core Data in the download methods...

Have a look at Concurrency with Core Data. You will need to tweak a bit your code by at least using a separate managed object context for your secondary thread (I don't know if it is feasible for you to create the moc there).

Also have a look at this tutorial from Cocoa is my Girlfriend.

As an alternative to all that, you could consider doing:

if (buttonIndex==1) {
    [self showActivityViewer];
    [self performSelector:@selector(startDownloads) withObject:nil afterDelay:0];
    NSLog(@"AGGIORNA");
} else {
    NSLog(@"NON AGGIORNARE");
    return;
}

with:

-(void)startDownloads {
    [self downloadControlAndUpdatePoi];
    [self downloadControlAndUpdateItinerari];
    [self downloadControlAndUpdateArtisti];
    [self downloadControlAndUpdateEventi];
    [self hideActivityViewer];  
}

This does not use threads at all, but I am not sure that the activity viewer will show without any glitch. One more level of hack, if needed, and you could specify a delay in

[self performSelector:@selector(startDownloads) withObject:nil afterDelay:0.1];
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文