【问题标题】:How To Update ProgressView in Async Thread如何在异步线程中更新 ProgressView
【发布时间】:2013-07-29 08:17:06
【问题描述】:

我对 iOS 中 GCD 和队列的组合几乎没有疑问。让我把它们放在这里

首先。根据 IOS6 编程手册,这里是使用异步和同步下载图像的小代码 sn-p。及其如下

dispatch_queue_t concurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);dispatch_async (concurrentQueue, ^ {
UIImage *image = nil;
dispatch_sync(concurrentQueue, ^ { /* download image here */ });
dispatch_sync(dispatch_get_main_queue, ^ { /* show the downloaded image here */ });
});

第二。我需要从 Url 下载几张图片,同时我需要更新进度视图以显示已下载多少张图片。所以我使用以下代码sn-p

dispatch_queue_t concurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async (concurrentQueue, ^ { dispatch_sync(dispatch_get_main_queue, ^ { /* download image here and update progress view here */ });
});

请注意,我正在更新主线程上的进度视图。但是在下载所有图像后,我的进度视图会显示出来。我检查了其他链接,但答案不是很清楚。没有得到问题的原因。我的代码如下。

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{dispatch_sync(dispatch_get_main_queue(), ^{
       alert = [[UIAlertView alloc] initWithTitle:@"Please Wait Downloading reports..." message:nil delegate:self cancelButtonTitle:nil otherButtonTitles: nil] ;
       prgView = [[UIProgressView alloc]initWithProgressViewStyle:UIProgressViewStyleBar];
       prgView.frame = CGRectMake(10, 50, 270, 20);
       [alert addSubview:prgView];
       [alert show];
      prgView.progress=0.0;
       actual= 0.0;

       for(NSInteger i=0; i<[fileLinks count];i++)
       {

        //  [self downLoadFileFromUrl : fileLinks[i]: i ];
           NSLog(@"Url is %@", fileLinks[i]);
           NSLog(@"i value is %d", i);
           NSData *imagedata = [NSData dataWithContentsOfURL:[NSURL URLWithString:fileLinks[i]]];
           NSString * _filename=  [fileLinks[i] lastPathComponent];
           NSMutableString *filePath = [[NSMutableString alloc] initWithFormat:@"%@", [VSCore getTempFolder]];
           [filePath appendString: _filename] ;
           [imagedata writeToFile: filePath atomically:YES];
           prgView.progress =((float) i /([fileLinks count]));
           NSLog(@" progress value is %f", prgView.progress);

       }
   });
});

那么这里有什么问题?我的另一个疑问是,如果有人想在执行长时间处理任务时保持用户(显示进度视图或活动指示器),他们不能在主线程中这样做吗? (假设它们显示进度条或活动指示器),为什么我们必须使用异步、与并发线程或主线程同步等的组合。

我有点困惑,在什么情况下我必须使用异步、同步、主队列、并发队列、串行队列的正确组合。如果有人给我解释或为我提供了解异步、同步和 3 队列的组合的好链接。那会很棒。

【问题讨论】:

  • 请格式化所有代码。在开始下载图片之前,您似乎要切换回主线程。
  • @Wain,我不明白你……你能简单解释一下吗?

标签: ios grand-central-dispatch


【解决方案1】:

您应该在主队列上创建和更新 `UIProgressView'

然后在另一个队列(不是主队列)上做你的工作

在工作时,如果你想更新 GUI 上的任何对象,你应该在主队列上进行

你可以这样做:

alert = [[UIAlertView alloc] initWithTitle:@"Please Wait Downloading reports..." message:nil delegate:self cancelButtonTitle:nil otherButtonTitles: nil] ;
    prgView = [[UIProgressView alloc]initWithProgressViewStyle:UIProgressViewStyleBar];
    prgView.frame = CGRectMake(10, 50, 270, 20);
    [alert addSubview:prgView];
    [alert show];
    prgView.progress=0.0;
    actual= 0.0;

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        for(NSInteger i=0; i<[fileLinks count];i++)
        {
            //  [self downLoadFileFromUrl : fileLinks[i]: i ];
            NSLog(@"Url is %@", fileLinks[i]);
            NSLog(@"i value is %d", i);
            NSData *imagedata = [NSData dataWithContentsOfURL:[NSURL URLWithString:fileLinks[i]]];
            NSString * _filename=  [fileLinks[i] lastPathComponent];
            NSMutableString *filePath = [[NSMutableString alloc] initWithFormat:@"%@", [VSCore getTempFolder]];
            [filePath appendString: _filename] ;
            [imagedata writeToFile: filePath atomically:YES];

            dispatch_async(dispatch_get_main_queue(), ^{
                prgView.progress =((float) i /([fileLinks count]));
            });
            NSLog(@" progress value is %f", prgView.progress);
        }
    });

您可以阅读一本电子书:“Pro.Multithreading.and.Memory.Management.for.iOS.and.OS.X”(购买或询问 gg)

祝你好运!

【讨论】:

  • 感谢 NtVietHung 的回答,但是在 for 循环中创建异步不会创建那么多线程?可以在 for 循环中创建异步吗?
  • 不...它不会创建任何线程。此命令:dispatch_async(dispatch_get_main_queue(), ^{...}) 将调用 main queue。您可以在应用程序运行时进行调试以查看有多少线程:D
  • 它将创建 一个 线程,该线程大部分被阻塞 - 因为dataWithContentsOfURL: 基本上是一个异步任务包装到一个同步方法中。这是一个臭名昭著的反模式。我强烈建议在处理网络请求时切换到异步模式,如果您想改进代码并希望对设备的受限功能更加友好。
【解决方案2】:

如果您仔细查看您的代码,您会发现您实际上是在主线程中运行所有内容。

^{dispatch_sync(dispatch_get_main_queue(), ^{
       alert = [[UIAlertView alloc] initWithTitle:@"Please Wait Downloading reports..." message:nil delegate:self cancelButtonTitle:nil otherButtonTitles: nil] ;
       prgView = [[UIProgressView alloc]initWithProgressViewStyle:UIProgressViewStyleBar];
       prgView.frame = CGRectMake(10, 50, 270, 20);
       [alert addSubview:prgView];
       [alert show];
      prgView.progress=0.0;
       actual= 0.0;
       for(NSInteger i=0; i<[fileLinks count];i++)
       {

        //  [self downLoadFileFromUrl : fileLinks[i]: i ];
           NSLog(@"Url is %@", fileLinks[i]);
           NSLog(@"i value is %d", i);
           NSData *imagedata = [NSData dataWithContentsOfURL:[NSURL URLWithString:fileLinks[i]]];
           NSString * _filename=  [fileLinks[i] lastPathComponent];
           NSMutableString *filePath = [[NSMutableString alloc] initWithFormat:@"%@", [VSCore getTempFolder]];
           [filePath appendString: _filename] ;
           [imagedata writeToFile: filePath atomically:YES];
           prgView.progress =((float) i /([fileLinks count]));
           NSLog(@" progress value is %f", prgView.progress);

       }
   });

这将在主线程中运行 downloadFileFromUrl 和进度视图的更新(除非 downloadFileFromUrl 是异步操作,我不知道)

基本上你需要在辅助线程中运行downloadFileFromUrl方法,然后只在主线程中运行这个位:prgView.progress =((float) i /([fileLinks count]));

【讨论】:

  • 哦...你不应该这样做。通过这种方式,主队列在您的工作进行时会很忙。您的 GUI 将冻结,您将永远不会看到“prgView.progress”的变化
  • 只是为了澄清一点,我写的代码不是一个正确的解决方案!这只是问题中的代码,删除了对 dispatch_async 的初始调用。我只是为了清楚起见才添加。
  • dispatch_sync(dispatch_get_main_queue() - 导致死锁。切勿在主队列中调用 dispatch_sync!stackoverflow.com/questions/12379059/…
【解决方案3】:

async和sync结合是对的,但是主线程和其他线程结合会导致循环,尤其是在async线程中使用sync主线程。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-07-10
    • 1970-01-01
    • 2020-08-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多