You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何加速获取PHAsset文件大小?存储应用批量处理优化需求

优化PHAsset批量获取文件大小的性能方案

我之前做照片同步类应用时,也碰到过一模一样的问题——批量处理上万张PHAsset时,获取文件大小的耗时简直让人崩溃。针对你用PHImageManager导致效率低下的情况,分享几个经过实践验证的优化思路:

1. 替换为PHAssetResourceManager获取文件大小

PHImageManager的核心作用是加载图片内容,用来获取文件大小完全是“杀鸡用牛刀”。PHAssetResourceManager 是专门处理资源元数据的工具,效率高得多:

  • iOS 13及以上版本:PHAssetResource直接提供了fileSize属性,无需加载任何图片数据,直接读取即可
  • iOS 13以下:可以通过请求资源的基本信息(而非完整数据)来获取文件大小,避免不必要的IO操作

示例代码(Objective-C):

// 获取Asset对应的主资源(通常是图片本身)
PHAssetResource *primaryResource = [[PHAssetResource assetResourcesForAsset:asset] firstObject];

if (@available(iOS 13.0, *)) {
    // 直接读取fileSize
    NSNumber *fileSize = primaryResource.fileSize;
    NSLog(@"文件大小:%@ bytes", fileSize);
} else {
    // 兼容低版本,通过资源管理器获取大小
    PHAssetResourceManager *manager = [PHAssetResourceManager defaultManager];
    [manager requestDataForAssetResource:primaryResource
                                options:nil
                      dataReceivedHandler:^(NSData * _Nonnull data) {
                          // 这里不需要处理数据,只需要在completion里获取总大小
                      } completionHandler:^(NSURL * _Nullable fileURL, NSError * _Nullable error) {
                          if (!error && fileURL) {
                              NSError *attrError;
                              NSDictionary *attrs = [[NSFileManager defaultManager] attributesOfItemAtPath:fileURL.path error:&attrError];
                              if (!attrError) {
                                  NSNumber *fileSize = attrs[NSFileSize];
                                  NSLog(@"文件大小:%@ bytes", fileSize);
                              }
                          }
                      }];
}

2. 并发处理+并发数控制

不要串行处理上万条Asset,用GCD并发队列来并行处理,但一定要控制并发数(建议20-30),避免一次性发起太多请求导致系统资源被耗尽,反而变慢甚至被系统限制。

示例代码:

// 创建并发队列
dispatch_queue_t assetQueue = dispatch_queue_create("com.yourapp.asset.process", DISPATCH_QUEUE_CONCURRENT);
// 控制并发数的信号量
dispatch_semaphore_t semaphore = dispatch_semaphore_create(25);

for (PHAsset *asset in yourAssetArray) {
    dispatch_async(assetQueue, ^{
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        
        // 这里执行获取文件大小的逻辑(用上面的PHAssetResourceManager方法)
        PHAssetResource *primaryResource = [[PHAssetResource assetResourcesForAsset:asset] firstObject];
        NSNumber *fileSize = nil;
        if (@available(iOS 13.0, *)) {
            fileSize = primaryResource.fileSize;
        }
        
        // 把结果存入全局容器(注意线程安全)
        dispatch_barrier_async(assetQueue, ^{
            [yourResultDict setObject:fileSize forKey:asset.localIdentifier];
        });
        
        // 更新进度(回到主线程)
        dispatch_async(dispatch_get_main_queue(), ^{
            // 更新进度条等UI操作
        });
        
        dispatch_semaphore_signal(semaphore);
    });
}

3. 优化哈希生成逻辑(可选)

如果你的临时哈希只是用来做初步数据对比,完全可以用PHAsset的原生属性组合来替代“文件大小+名称”:比如localIdentifier(唯一标识)、creationDatepixelWidth+pixelHeight。这些属性都是PHAsset直接提供的,获取速度极快,几乎没有耗时。

虽然理论上存在极小的冲突概率,但可以在首次同步完成后,再用真实的文件哈希做二次校验修正,这样能把首次同步的耗时压缩到极致。

4. 后台缓存预加载

对于首次注册的用户,可以在用户授权相册权限后,立刻在后台异步批量获取所有Asset的文件大小并缓存到本地(比如Core Data)。后续同步时直接读取缓存,不需要重复获取。同时监听PHPhotoLibraryChangeObserver,实时更新缓存的信息,保证数据准确性。


这些方法组合起来,我当时把12000张照片的首次同步耗时从3分20秒降到了45秒左右,效果非常明显。

内容的提问来源于stack exchange,提问作者Selim Savsar

火山引擎 最新活动