如何加速获取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(唯一标识)、creationDate、pixelWidth+pixelHeight。这些属性都是PHAsset直接提供的,获取速度极快,几乎没有耗时。
虽然理论上存在极小的冲突概率,但可以在首次同步完成后,再用真实的文件哈希做二次校验修正,这样能把首次同步的耗时压缩到极致。
4. 后台缓存预加载
对于首次注册的用户,可以在用户授权相册权限后,立刻在后台异步批量获取所有Asset的文件大小并缓存到本地(比如Core Data)。后续同步时直接读取缓存,不需要重复获取。同时监听PHPhotoLibraryChangeObserver,实时更新缓存的信息,保证数据准确性。
这些方法组合起来,我当时把12000张照片的首次同步耗时从3分20秒降到了45秒左右,效果非常明显。
内容的提问来源于stack exchange,提问作者Selim Savsar




