iOS - 后台进程和界面更新

3

问题很简单:我的应用程序每次启动时都会检查更新。如果有更新,就会弹出一个选择是或否的弹出窗口。当用户点击是时,将启动这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方法运行并显示旋转器。

谢谢:)

1个回答

5

首先,你不需要启动4个操作,因为你已经在一个次要线程中,并且不需要让这4个操作并行执行。你可以简单地执行:

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

首先,在startDownloads方法中,如果你在downloadControl*方法中使用了autorelease,则需要定义一个自动释放池,否则可能会出现内存泄漏。

至于为什么活动指示器没有显示出来,这取决于你是否调用了:

    [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... 请查看使用Core Data的并发。您需要对代码进行微调,至少为辅助线程使用单独的托管对象上下文(我不知道在那里创建moc是否可行)。 还可以参考Cocoa is my Girlfriend的本教程。 作为所有这些的替代方案,您可以考虑执行:
if (buttonIndex==1) {
    [self showActivityViewer];
    [self performSelector:@selector(startDownloads) withObject:nil afterDelay:0];
    NSLog(@"AGGIORNA");
} else {
    NSLog(@"NON AGGIORNARE");
    return;
}

使用:

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

这并未使用线程,但我不能确定活动查看器是否能够无故障显示。如果需要,可以进行更多级别的黑客攻击,你可以指定延迟时间。
[self performSelector:@selector(startDownloads) withObject:nil afterDelay:0.1];

但是我该如何调用startDownloads?在用户点击“是”后,使用NSThread分离还是[self startDownloads]? - Andrea Mario Lufino
抱歉,使用detachThread的方式是正确的。唯一的问题是,在startDownloads中您不需要更多的异步操作,它们可以按顺序执行。 - sergio
看起来一切都很好,但是出现了一个大问题:我的downloadAndControl*方法使用CoreData,并且使用这种解决方案,我在CoreData的save方法中遇到了NSInternalInconsistencyException异常。 - Andrea Mario Lufino
太好了,问题解决了!!!使用CoreData的方法必须由主线程运行,因此我使用performSelectorOnMainThread来运行这些方法。非常感谢sergio提供的精彩答案! :) - Andrea Mario Lufino

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