如何利用GCD Group批量下载文件

本文提供了一个使用GCD(Grand Central Dispatch)的iOS程序示例,展示如何异步并发下载多张图片,并在所有图片下载完成后进行相应处理。通过代码实现和详细说明,帮助开发者理解并应用GCD来优化多任务处理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

未命名未命名

小引:有时候,我们在开发iOS程序时,需要批量下载一些文件(比如图片),只有当全部文件下载完毕,我们才做相应的处理(界面更新,通知用户等)——也就是说虽然有多个文件在下载,但是我们只需要收到一个全部下载完毕的通知。

在网上搜索了一番,感觉使用GCD的高级功能Group,比较方便。下面写了一个小Demo,实现了多个图片文件的异步并发下载,缓存到本地,并显示到界面中。

参考了唐巧的一篇博文:使用GCD

另外感兴趣的同学可以看看下面几篇GCD相关文章,非常不错:

raywenderlich:Multithreading and Grand Central Dispatch on iOS for Beginners Tutorial

苹果官网:Grand Central Dispatch (GCD) Reference  Concurrency Programming Guide

Demo可以到Github上下载:

 https://github.com/BeyondVincent/DownloadImage_GCD

下面是使用GCD Group的关键代码:

- (IBAction)downloadAction:(UIButton *)sender
{
    [self resetImage];
    self.status.text = @"正在下载";
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_group_t downloadImage = dispatch_group_create();

    for (ImageInfo *info in self.imageList) {
        NSString* imagePath = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0] stringByAppendingPathComponent:info.imageName];

        NSFileManager *fileManager = [NSFileManager defaultManager];
        // 如果本地不存在图片,则从网络中下载
        if (![fileManager fileExistsAtPath:imagePath]) {
            dispatch_group_async(downloadImage, queue, ^{
                NSLog(@"Starting image download:%@", imagePath);
                // URL组装和编码
                NSString *urlString = [NSString stringWithFormat:@"%@/%@", self.baseUrl, info.imageName];
                NSURL *url = [NSURL URLWithString:[urlString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
                NSLog(@"image download from url:%@", urlString);

                // 开始下载图片
                NSData *responseData = [NSData dataWithContentsOfURL:url];
                // 将图片保存到指定路径中
                [responseData writeToFile:imagePath atomically:YES];
                // 将下载的图片赋值给info
                info.image = [UIImage imageWithData:responseData];
                NSLog(@"image download finish:%@", imagePath);
            });
        } else { // 将本地图片加载到systemInfo.MyImage
            info.image = [UIImage imageWithData:[NSData dataWithContentsOfFile:imagePath]];
        }
    }
    dispatch_group_notify(downloadImage, dispatch_get_main_queue(), ^{
        // 图片加载完毕之后,显示出来
        self.status.text = @"图片文件下载并缓存完毕";
        [self showImage];
    });
}

上面关键的代码是dispatch_group_async(并行执行线程1)和dispatch_group_notify(全部下载完毕,由此进行回调通知)。在for语句中循环开启了6个并发任务。当6个任务完成之后,调用showImage方法,将图片显示出来。
下面是运行效果图(第一个图为程序刚刚启动时的效果,第二个为点击开始异步下载图片按钮之后的效果):
QQ20130502-3QQ20130502-3

QQ20130502-2QQ20130502-2
 

在写本Demo的时候,遇到了以下两个问题

  1. 关于图片的加载,UIImage中的imageNamed:方法只能加载程序main bundle中的图片。要想加载Document中的图片,需要使用UIImage的imageWithData方法。更多相关资料可以阅读苹果官方介绍:UIImage
  2. NSURL URLWithString:myString returns Nil。在初始化NSURL实例对象是,一直都返回Nil。后来在这里(Here)发现原来是URL中含有特殊字符,需要进行编码处理,照着链接中的方法搞定。

_________________

本文由破船原创
转载请注明出处:BeyondVincent的博客
_________________

import json import datetime import os from qfluentwidgets import InfoBar import re import subprocess from PyQt5.QtWidgets import QApplication def get_adb_devices(): """ 获取已连接ADB设备列表 """ result = subprocess.run( ['adb', 'devices'], capture_output=True, text=True, encoding='utf-8' ) return [ line.split('\t')[0] for line in result.stdout.splitlines() if '\tdevice' in line ] def get_apk_versions(device): """ 获取指定设备的APK版本信息 """ # 获取所有包名 cmd = f"adb -s {device} shell pm list packages" packages = [ line.split(':', 1)[1] for line in subprocess.run( cmd, shell=True, capture_output=True, text=True, encoding='utf-8' ).stdout.splitlines() if line.startswith('package:') ] # 获取每个包版本 versions = [] for pkg in packages: cmd = f"adb -s {device} shell dumpsys package {pkg}" output = subprocess.run( cmd, shell=True, capture_output=True, text=True, encoding='utf-8' ).stdout match = re.search(r'versionName=([\d.]+)', output) versions.append({pkg: match.group(1) if match else None}) return versions class TestDataExporter: def __init__(self): self.devices = get_adb_devices() self.apk_data = {device: get_apk_versions(device) for device in self.devices} def export(self): """ 执行数据导出 """ try: data = { "export_time": datetime.datetime.now().isoformat(), "device_count": len(self.devices), "devices": self.devices, "apk_versions": self.apk_data } os.makedirs("TestData", exist_ok=True) filename = f"TestData@{datetime.datetime.now().strftime('%Y%m%d_%H%M%S')}.json" with open(os.path.join("TestData", filename), 'w', encoding='utf-8') as f: json.dump(data, f, indent=2, ensure_ascii=False) InfoBar.success("导出成功", "数据已保存至TestData目录", duration=2000) except Exception as e: InfoBar.error("导出失败", f"错误原因: {str(e)}", duration=3000) if __name__ == '__main__': app = QApplication([]) exporter = TestDataExporter() exporter.export() app.exec_(),“apksVersion”: [{“com.google.android.providers.media.module”: “14-11533485”}, {“com.google.android.overlay.modules.permissioncontroller.forframework”: “1.0”}, {“com.android.calllogbackup”: “13”}, {“com.uxto.gcd”: “1.0.8”}, {“com.hmdglobal.app.legalinformation”: “10.13.44501.01”}, {“com.google.android.overlay.gmsconfig.comms”: “1.0”}]需要导出这种手机中所有apk版本,按照这种格式,请优化下上面的代码
最新发布
04-02
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值