等待结果而不停止UI

N S*_*N S 1 iphone ipad ios

我有一个iPad应用程序,我想这样做

-(IBAction) clicked {
    image=download(@"http://....."); // this is on the main thread
}
Run Code Online (Sandbox Code Playgroud)

下载功能将调用一大堆非阻塞函数从Internet下载文件,但下载本身不应该返回,直到下载图像.

当程序在上面的image = download(...)行等待下载时,我希望UI能够仍然运行,例如能够滚动UITableView,单击另一个按钮等.

所以我在下载函数中使用的是RunLoop

-(void) download:(NSString *)url 
{
    BOOL stillDownloading=TRUE;
    while(stillDownloading) {
        stillDownloading=downloadAFwBytes(...);
        CFRunLoopRunInMode(kCFRunLoopCommonModes, 0, YES);
    }
}
Run Code Online (Sandbox Code Playgroud)

我认为CFRunLoopRunInMode函数将继续抽取UI消息,触摸,滚动主UI线程,以便UI继续工作,直到下载完成后才冻结,但由于某种原因,它只能工作很短的时间,最终用户界面冻结.

你知道为什么,或者如何解决?

下载函数在程序中随处调用,期望它等待下载,因此我暂时无法将其更改为非阻塞.

XJo*_*nes 5

你的问题的直接答案是,不,这不是什么CFRunLoopRunInMode.你有效地尝试做的是使当前的运行循环"产量",以便在加载操作继续时继续执行.这不是iOS和运行循环的工作方式.您的下载功能会阻止其下载的线程,直到下载完成为止,因此您的问题的唯一解决方案是更改实施,以便在后台线程上进行下载,并在完成时通知关注的对象.这是一个相对较小的变化,可以让你走上正确的轨道.这个整体主题(并发,管理后台任务)是一个更大的讨论,有不同的考虑/权衡.我会切入追逐,希望能让你走上正轨.

  1. 定义NSNotification一下你的下载方法可以发布感兴趣的对象观察的一对:

    // in the .h file of the class performing the download
    extern NSString * const MyClassLoadingDidStartNotification;
    extern NSString * const MyClassLoadingDidFinishNotification;
    
    // in the .m file of the class performing the download
    NSString * const MyClassLoadingDidStartNotification = @"MyClassLoadingDidStart";
    NSString * const MyClassLoadingDidFinishNotification = @"MyClassLoadingDidFinish";
    
    Run Code Online (Sandbox Code Playgroud)
  2. 在下载例程中,在后台进行下载并发布相应的通知:

    -(void) download:(NSString *)url 
    {
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            [[NSNotificationCenter defaultCenter] postNotificationName:MyClassLoadingDidStartNotification object:self];
            BOOL stillDownloading=TRUE;
            while(stillDownloading) {
                stillDownloading=downloadAFwBytes(...);
            }
            [[NSNotificationCenter defaultCenter] postNotificationName:MyClassLoadingDidFinishNotification object:self];
        });
    }
    
    Run Code Online (Sandbox Code Playgroud)
  3. 在任何启动下载的对象中,观察并处理通知

    // in any class that initiates a download
    - (void)init...
    {
        self = [super init...];
        if (self) {
            // other initialization
            [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didStartLoading:) name:MyClassLoadingDidStartNotification object:nil];
            [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didFinishLoading:) name:MyClassLoadingDidFinishNotification object:nil];
        }
        return self;
    }
    
    - (void)dealloc
    {
        [[NSNotificationCenter defaultCenter] removeObserver:self name:MyClassLoadingDidStartNotification object:nil];
        [[NSNotificationCenter defaultCenter] removeObserver:self name:MyClassLoadingDidFinishNotification object:nil];
    }
    
    - (void)didStartLoading:(NSNotification *)notification
    {
        // update UI to show loading status (make sure you do UI changes on main thread)
        // optionally check notification.object to ensure it's the loader class instance you care about
    }
    
    - (void)didFinishLoading:(NSNotification *)notification
    {
        // update UI to show loading status (make sure you do UI changes on main thread)
        // optionally check notification.object to ensure it's the loader class instance you care about
    }
    
    Run Code Online (Sandbox Code Playgroud)

请记住,这是一个非常基本的起点.随着您了解更多并决定您需要什么,您一定会定制它.例如,您可能希望添加错误处理,限制并发加载操作,提供其他加载状态等.