当前位置: 首页 > news >正文

iOS即时通讯发送图片消息内存暴涨优化

问题:

即时通讯App在发送图片消息时内存暴涨导致网络请求初始化失败(内存不足OOM),发送消息失败。

可能原因分析:

  1. 有内存泄漏。
  2. 发送的图片消息,可能包含大图,没有进行压缩处理,导致内存占用过高。
  3. 在发送过程中,可能同时进行了图片的读取和处理,如果图片很大,处理过程中会产生很大的内存峰值。

解决方案:

  1. 对于发送消息时的内存暴涨:
  • 检查是否有内存泄漏,使用Instruments工具检测。

Instruments工具检测结果

检测发现确实有内存泄漏,解决后发现问题还是存在。

  • 在发送图片消息前,对图片进行压缩(包括压缩质量和尺寸),然后再发送压缩后的图片。

  • 避免直接操作大图,可以使用后台线程进行处理,防止阻塞主线程。

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{// 1. 读取图片(避免使用imageNamed:)UIImage *originalImage = [UIImage imageWithContentsOfFile:filePath];// 2. 尺寸压缩(限制最大边长为1024)CGFloat maxSize = 1024.0;CGSize scaledSize = [self scaledSizeForImage:originalImage maxLength:maxSize];// 3. 质量压缩(70%质量)UIImage *compressedImage = [self resizeImage:originalImage toSize:scaledSize];NSData *imageData = UIImageJPEGRepresentation(compressedImage, 0.7);// 4. 发送压缩后的数据(非原始图片)[self sendImageData:imageData];
});// 计算缩放尺寸
- (CGSize)scaledSizeForImage:(UIImage *)image maxLength:(CGFloat)maxLength {CGFloat ratio = MIN(maxLength / image.size.width, maxLength / image.size.height);return CGSizeMake(image.size.width * ratio, image.size.height * ratio);
}// 图片重绘
- (UIImage *)resizeImage:(UIImage *)image toSize:(CGSize)targetSize {UIGraphicsBeginImageContextWithOptions(targetSize, NO, UIScreen.mainScreen.scale);[image drawInRect:CGRectMake(0, 0, targetSize.width, targetSize.height)];UIImage *resizedImage = UIGraphicsGetImageFromCurrentImageContext();UIGraphicsEndImageContext();return resizedImage;
}
  • 压缩后的图片数据要及时释放不必要的资源,避免在内存中同时存在多张图片,比如在图片展示后,如果原图不再需要,可以将其置为nil,帮助内存回收。(@autoreleasepool)。

处理到此内存暴涨解决了,但是随着发送图片内存还是在持续增加,现在每发送一张图片内存还是要涨10M。(message.compressRatio = 1.0 // 设置压缩率),message.compressRatio = 0.2,每发送一张图片内存也要涨5M。

新问题:

即时通讯App在发送图片消息时每次展示一张图片内存涨10M多。

可能原因分析:

1.图片加载方法不对

Cell图片展示

[UIImage imageNamed:]的内存缓存特性:

  • 系统级缓存无法自动释放

  • 特别不适合大图和列表展示场景

  • 会自动缓存图片到系统缓存

  • 适合重复使用的小图标

  • 不适合大图或单次使用的图片

2.内存增长原因:

为了支持 GIF/WebP 等动图格式,showImageView为SDAnimatedImageView,它解码后的帧缓存会增加内存占用。

  • 大图被缓存且无法及时释放

  • 图片解码后的位图数据占用内存

3.Cell 复用机制

快速滑动时可能同时加载多张大图,旧图片未及时释放

优化方案:

通过以下优化措施,图片展示内存问题应该能得到显著改善。核心要点是:

  • 避免使用 imageNamed: 加载大图

  • 合理配置 SDAnimatedImageView

  • 完善 cell 复用机制

  • 使用图片下采样技术

  • 滑动时优化资源使用

1.使用正确的图片加载方式,用 [UIImage imageWithContentsOfFile:filePath]替代[UIImage imageNamed:filePath]

优点:

  • 不会缓存图片

  • 适合大图和单次使用的图片

后台线程解码 + 尺寸适配

// 后台线程处理图片
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{@autoreleasepool {// 1. 从文件加载UIImage *originalImage = [UIImage imageWithContentsOfFile:filePath];// 2. 压缩图片尺寸 (按需)CGSize targetSize = CGSizeMake(800, 800); // 根据需求调整UIGraphicsBeginImageContextWithOptions(targetSize, NO, [UIScreen mainScreen].scale);[originalImage drawInRect:CGRectMake(0, 0, targetSize.width, targetSize.height)];UIImage *scaledImage = UIGraphicsGetImageFromCurrentImageContext();UIGraphicsEndImageContext();// 3. 主线程更新UIdispatch_async(dispatch_get_main_queue(), ^{self.showImageView.image = scaledImage;});}
});

2.使用 ImageIO 框架高效加载

#import <ImageIO/ImageIO.h>NSURL *imageURL = [NSURL fileURLWithPath:filePath];
NSDictionary *options = @{(id)kCGImageSourceShouldCache: @NO}; // 禁用解码缓存
CGImageSourceRef source = CGImageSourceCreateWithURL((CFURLRef)imageURL, NULL);
CGImageRef imageRef = CGImageSourceCreateImageAtIndex(source, 0, (CFDictionaryRef)options);
UIImage *image = [UIImage imageWithCGImage:imageRef];
CGImageRelease(imageRef);
CFRelease(source);self.showImageView.image = image;

3.使用第三方图片加载库

// 使用SDWebImage示例
#import <SDWebImage/SDWebImage.h>[self.showImageView sd_setImageWithURL:[NSURL fileURLWithPath:filePath]placeholderImage:nilcompleted:^(UIImage *image, NSError *error,SDImageCacheType cacheType, NSURL *imageURL) {// 加载完成回调}];

4.优化 SDAnimatedImageView 配置

- (SDAnimatedImageView *)showImageView {if (!_showImageView) {_showImageView = [[SDAnimatedImageView alloc] init];_showImageView.contentMode = UIViewContentModeScaleAspectFill;_showImageView.userInteractionEnabled = YES;// 添加以下优化配置_showImageView.shouldIncrementalLoad = YES; // 渐进式加载_showImageView.maxBufferSize = 1024 * 1024; // 设置合理的缓冲区大小_showImageView.runLoopMode = NSDefaultRunLoopMode; // 滑动时暂停动画}return _showImageView;
}

5.Cell 复用时的内存管理

// 在 cell 的 prepareForReuse 中清理
- (void)prepareForReuse {[super prepareForReuse];// 停止动画并释放资源[self.showImageView stopAnimating];self.showImageView.currentFrame = nil;self.showImageView.animationImages = nil;// 取消未完成的图片加载[self.showImageView sd_cancelCurrentImageLoad];
}

6.图片尺寸优化(针对大图)

// 使用 ImageIO 进行下采样
- (UIImage *)downsampleImageAtPath:(NSString *)path toSize:(CGSize)size {NSURL *url = [NSURL fileURLWithPath:path];NSDictionary *options = @{(id)kCGImageSourceShouldCache: @NO,(id)kCGImageSourceShouldAllowFloat: @YES};CGImageSourceRef source = CGImageSourceCreateWithURL((CFURLRef)url, NULL);CGFloat maxDimension = MAX(size.width, size.height) * [UIScreen mainScreen].scale;NSDictionary *downsampleOptions = @{(id)kCGImageSourceCreateThumbnailFromImageAlways: @YES,(id)kCGImageSourceShouldCacheImmediately: @YES,(id)kCGImageSourceThumbnailMaxPixelSize: @(maxDimension)};CGImageRef imageRef = CGImageSourceCreateThumbnailAtIndex(source, 0, (CFDictionaryRef)downsampleOptions);UIImage *image = [UIImage imageWithCGImage:imageRef];if (imageRef) CFRelease(imageRef);if (source) CFRelease(source);return image;
}

7.滑动性能优化

// 在 scrollView 代理中实现以下方法
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {// 暂停屏幕外 cell 的动画for (UITableViewCell *cell in self.tableView.visibleCells) {if ([cell isKindOfClass:[YourCellClass class]]) {YourCellClass *yourCell = (YourCellClass *)cell;[yourCell.showImageView startAnimating];}}// 暂停非可见 cell 的动画NSArray *visiblePaths = [self.tableView indexPathsForVisibleRows];for (NSIndexPath *indexPath in self.loadedIndexPaths) {if (![visiblePaths containsObject:indexPath]) {YourCellClass *cell = (YourCellClass *)[self.tableView cellForRowAtIndexPath:indexPath];[cell.showImageView stopAnimating];}}
}

其他优化建议:

1.内存警告处理

- (void)didReceiveMemoryWarning {[super didReceiveMemoryWarning];// 清除所有图片缓存[[SDImageCache sharedImageCache] clearMemory];
}

2.使用合适的 SDWebImage 选项:

[self.showImageView sd_setImageWithURL:imageURLplaceholderImage:niloptions:SDWebImageAvoidDecodeImage | SDWebImageScaleDownLargeImages |SDWebImageProgressiveLoadcompleted:nil];

3.监控内存使用:

- (void)monitorMemoryUsage {struct task_basic_info info;mach_msg_type_number_t size = sizeof(info);kern_return_t kerr = task_info(mach_task_self(),TASK_BASIC_INFO,(task_info_t)&info,&size);if (kerr == KERN_SUCCESS) {NSLog(@"Memory in use (in MB): %f", info.resident_size / 1024.0 / 1024.0);}
}

4.配置 SDWebImage 全局参数(AppDelegate 中)

// 设置全局缓存策略
SDImageCacheConfig *cacheConfig = [SDImageCacheConfig defaultCacheConfig];
cacheConfig.maxMemoryCost = 100 * 1024 * 1024; // 100MB 内存缓存
cacheConfig.maxMemoryCount = 50; // 最大缓存图片数量
cacheConfig.shouldDecompressImages = NO; // 禁止自动解压
[SDImageCache sharedImageCache].config = cacheConfig;

性能对比

相关文章:

  • 10.C S编程错误分析
  • MySQL 自增主键 ID 设置为 0有坑
  • 二叉树的最大深度题解
  • Apache Kafka Connect任意文件读取漏洞(CVE-2025-27817)
  • AWS Config:概述、优势以及如何开始?
  • Jmeter的三种参数化方式详解
  • 【WPF】WPF 中 `DisplayMemberPath` 与 `SelectedValuePath` 的深入理解与实战应用
  • Kafka Connect架构深度解析:从分布式设计到组件交互机制
  • 对抗串扰的第一武器
  • 基于深度学习的智能视频内容理解系统:技术与实践
  • 【JSON-To-Video】AI智能体开发:为视频图片元素添加动效(滑入、旋转、滑出),附代码
  • 苍穹外卖--基于Spring Cache缓存套餐
  • PSP专辑17本PDF
  • Ubuntu设置nginx自启动
  • 力扣经典算法篇-17-反转字符串中的单词(逆序遍历,数组分割,正则表达式)
  • java 基础方法 list分页
  • Modern C++(五)初始化
  • 为什么晶振电路要并联1MΩ电阻?为什么有的并联了,有的又没有?
  • 华为云Flexus+DeepSeek征文 | 基于华为云ModelArts Studio搭建Chatbox AI聊天助手
  • 当 GitLab 服务器网络配置发生变化,如何修改
  • 没有公司可以做网站吗/手机导航下载2022新版
  • 佛山网站制作网站/北京竞价托管代运营
  • 如何设计网站建设方案/谷歌外贸网站推广
  • vi设计网站大全/百度推广的效果
  • 做网站接活全流程/网页友情链接
  • 电子商务网站建设与维护李建忠下载/产品推广哪个平台好