【iOS报错】iOS日常开发报错日志

写在前面

该篇文章主要记录日常 iOS 开发过程中遇到的报错或者是调试心得。

目录

[ImageManager] FigPhotoDecompressionContainerDecodeImageForIndexAsync -[PLFigPreheatItem startPreheatRequestWithCompletionHandler:]_block_invoke_2 returned -16991 from /var/mobile/Media/PhotoData/Thumbnails/V2/DCIM/112APPLE/IMG_2270.JPG/5005.JPG

具体报错问题

1、获取相册所有图片引起的内存暴增问题【2019.09.20】

日志打印:
备注:会重复打印多次该日志,直至系统因为内存暴增而 crash

[ImageManager] FigPhotoDecompressionContainerDecodeImageForIndexAsync -[PLFigPreheatItem startPreheatRequestWithCompletionHandler:]_block_invoke_2 returned -16991 from /var/mobile/Media/PhotoData/Thumbnails/V2/DCIM/112APPLE/IMG_2270.JPG/5005.JPG

问题描述:

要求是要获取相册中所有的图片数据进行展示。初始的写法是优先获取相册中的所有 PHAsset 数据对象,然后再通过遍历获取对应的缩略图,并切换到主线程进行展示。但是因为图片数量过多(当前测试为 14000 张图片),引起了内存暴增,最终 crash。

iPhoneX iOS12.1 环境下

初始代码如下:

/** 获取相册全部原图(PHAsset) **/
+ (NSArray *)getAllLocalOriginalImages {

    NSMutableArray *tempImages = [NSMutableArray array];

    // 获取所有智能相册
    PHFetchResult *smartAlbums = [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeSmartAlbum subtype:PHAssetCollectionSubtypeAlbumRegular options:nil];

    for (NSInteger i=0; i<smartAlbums.count; i++) {
        // 是否按创建时间排序
        PHFetchOptions *option = [[PHFetchOptions alloc] init];
        option.sortDescriptors = sortDescriptorWithKey:@"creationDate" ascending:YES]];
        option.predicate = [NSPredicate predicateWithFormat:@"mediaType == %ld", PHAssetMediaTypeImage];

        PHAssetCollection *assetCollection = smartAlbums[i];
        // 遍历获取相册
        if (assetCollection.assetCollectionSubtype == PHAssetCollectionSubtypeSmartAlbumUserLibrary) {
            PHFetchResult *fetchResult = [PHAsset fetchAssetsInAssetCollection:assetCollection options:nil];
            NSArray *assets;
            if (fetchResult.count > 0) {
                // 某个相册里面的所有 PHAsset 对象
                assets = [self getAllPhotosAssetInAblumCollection:assetCollection ascending:NO];
                [tempImages addObjectsFromArray:assets];
            }
        }
    }

    return tempImages;
}


#pragma mark - 根据 PHAsset 获取图片信息
/** 根据 PHAsset 获取图片信息 **/
+ (UIImage *)getImage:(PHAsset *)phAsset targetSize:(CGSize)targetSize {

    __block UIImage *image = nil;

    PHImageRequestOptions *options = [[PHImageRequestOptions alloc] init];
    options.networkAccessAllowed = YES;
    options.synchronous = YES;

    // 设置 resizeMode = PHImageRequestOptionsResizeModeExact, 否则返回的图片大小不一定是设置的 targetSize
    options.resizeMode = PHImageRequestOptionsResizeModeExact;

    // 设置 deliveryMode = PHImageRequestOptionsDeliveryModeHighQualityFormat,是保证 resultHandler 值回调一次,否则可能回回调多次,只有最后一次返回的图片大小等于设置的 targetSize
    options.deliveryMode = PHImageRequestOptionsDeliveryModeHighQualityFormat;

    PHImageManager *manager = [PHImageManager defaultManager];
    [manager requestImageForAsset:phAsset targetSize:targetSize contentMode:PHImageContentModeDefault options:options resultHandler:^(UIImage * _Nullable result, NSDictionary * _Nullable info) {
        image = result;
    }];

    return image;
}
错误代码:

#pragma mark - 获取图片数据源
/** 获取图片数据源 **/
- (void)getAllImages {

    dispatch_async(dispatch_queue_create("GetAllImages_Queue", DISPATCH_QUEUE_CONCURRENT), ^{
        /** 获取当前本地所有图片 **/
        self.imageDataArrayM = [NSMutableArray array];
        self.tempArrayM = [NSMutableArray arrayWithArray:[LHAPI getAllLocalOriginalImages]];
        CGSize targetSize = CGSizeMake(self.imageTargetW, self.imageTargetW);

        for (NSInteger i=0; i<self.tempArrayM.count; i++) {
            PHAsset *asset = self.tempArrayM[i];
            if (asset.pixelWidth < self.imageTargetW && asset.pixelHeight < self.imageTargetW) {
                targetSize = CGSizeMake(asset.pixelWidth, asset.pixelHeight);
            }

            LHLocalUploadModel *model = [[LHLocalUploadModel alloc] init];
            model.isVideo = NO;
            model.asset = asset;
            model.image = [LHAPI getImage:asset targetSize:targetSize];
            model.originIndex = i;
            model.selectIndex = 0;
            [self.imageDataArrayM addObject:model];

            // 让其添加到 第30个时就优先展示
            if (self.imageDataArrayM.count % self.firstShowNum == 0) {
                dispatch_async(dispatch_get_main_queue(), ^{
                    [self.imageCollectionView reloadData];
                });
            }
        }

        dispatch_async(dispatch_get_main_queue(), ^{
            NSLog(@"----------- 相册图片,共 %ld 张照片", self.imageDataArrayM.count);
            [self.imageCollectionView reloadData];
        });
    });
}

问题原因:

- (PHImageRequestID)requestImageForAsset:(PHAsset *)asset targetSize:(CGSize)targetSize contentMode:(PHImageContentMode)contentMode options:(nullable PHImageRequestOptions *)options resultHandler:(void (^)(UIImage *nullable result, NSDictionary *nullable info))resultHandler;

该方法在执行的时候会占用大量内存;而for 循环遍历了太多的数据,所以会引起内存暴增;
可以通过分页拉取图片数据,进而缓解内存暴增问题。

解决办法:

降低缩略图尺寸(即图片质量)、分页遍历图片数据,单次遍历数据量少量即可。

参考链接:
PHImageFileURLKey missing from .png images in iOS 12.1

解决后的代码如下:

#pragma mark - 获取图片数据源
/** 获取图片数据源 **/
- (void)getAllImages {

    dispatch_async(dispatch_queue_create("GetAllImages_Queue", DISPATCH_QUEUE_CONCURRENT), ^{
        /** 获取当前本地所有图片 **/
        self.imageDataArrayM = [NSMutableArray array];
        self.imageDataOriginArrayM = [NSMutableArray arrayWithArray:[LHAPI getAllLocalOriginalImages]];

        self.imagePageNum = 0;
        self.imagePageSize = 1000;

        [self reloadNewImageData];
    });
}

/** 当快滚动到底部时拉取新的数据 **/
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {

    if (scrollView == self.imageCollectionView) {
        CGFloat offsetY = self.imageCollectionView.contentOffset.y;
        CGFloat itemH = self.collectionItemW + 5.0f;
        NSInteger curIndex = (offsetY / itemH) * 4;
        if (curIndex > 0 && curIndex > self.imageDataArrayM.count - 50) {
            NSLog(@"curIndex ----------- %ld", curIndex);
            [self reloadNewImageData];
        }
    }
}

/** 获取更多数据源 **/
- (void)reloadNewImageData {

    if (self.imageDataArrayM.count == self.imageDataOriginArrayM.count) {
        return;
    }

    self.imagePageNum++;
    NSInteger curPageIndex = self.imageDataArrayM.count;
    NSInteger nextPageTotal = self.imageDataArrayM.count + self.imagePageNum * self.imagePageSize;
    nextPageTotal = nextPageTotal <= self.imageDataOriginArrayM.count ? nextPageTotal:self.imageDataOriginArrayM.count;
    CGSize targetSize = CGSizeMake(self.imageTargetW, self.imageTargetW);

    for (NSInteger i=curPageIndex; i<nextPageTotal; i++) {
        PHAsset *asset = self.imageDataOriginArrayM[i];
        if (asset.pixelWidth < self.imageTargetW && asset.pixelHeight < self.imageTargetW) {
            targetSize = CGSizeMake(asset.pixelWidth, asset.pixelHeight);
        }

        LHLocalUploadModel *model = [[LHLocalUploadModel alloc] init];
        model.isVideo = NO;
        model.asset = asset;
        model.image = [LHAPI getImage:asset targetSize:targetSize];
        model.originIndex = i;
        model.selectIndex = 0;
        [self.imageDataArrayM addObject:model];
    }

    dispatch_async(dispatch_get_main_queue(), ^{
        NSLog(@"----------- 相册图片,共 %ld 张照片", self.imageDataArrayM.count);
        [self.imageCollectionView reloadData];
    });
}
© 著作权归作者所有
这个作品真棒,我要支持一下!
该专栏主要用于记录日常iOS开发中的笔记心得,包括 Objective-C、Swift、Mac OS 开发及学习随笔。
0条评论
top Created with Sketch.