問題:
即時通訊App在發(fā)送圖片消息時內存暴漲導致網(wǎng)絡請求初始化失?。▋却娌蛔鉕OM),發(fā)送消息失敗。
可能原因分析:
- 有內存泄漏。
- 發(fā)送的圖片消息,可能包含大圖,沒有進行壓縮處理,導致內存占用過高。
- 在發(fā)送過程中,可能同時進行了圖片的讀取和處理,如果圖片很大,處理過程中會產(chǎn)生很大的內存峰值。
解決方案:
- 對于發(fā)送消息時的內存暴漲:
- 檢查是否有內存泄漏,使用Instruments工具檢測。

檢測發(fā)現(xiàn)確實有內存泄漏,解決后發(fā)現(xiàn)問題還是存在。
在發(fā)送圖片消息前,對圖片進行壓縮(包括壓縮質量和尺寸),然后再發(fā)送壓縮后的圖片。
避免直接操作大圖,可以使用后臺線程進行處理,防止阻塞主線程。
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. 發(fā)送壓縮后的數(shù)據(jù)(非原始圖片)
[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;
}
- 壓縮后的圖片數(shù)據(jù)要及時釋放不必要的資源,避免在內存中同時存在多張圖片,比如在圖片展示后,如果原圖不再需要,可以將其置為nil,幫助內存回收。(@autoreleasepool)。
處理到此內存暴漲解決了,但是隨著發(fā)送圖片內存還是在持續(xù)增加,現(xiàn)在每發(fā)送一張圖片內存還是要漲10M。(message.compressRatio = 1.0 // 設置壓縮率),message.compressRatio = 0.2,每發(fā)送一張圖片內存也要漲5M。
新問題:
即時通訊App在發(fā)送圖片消息時每次展示一張圖片內存漲10M多。
可能原因分析:
1.圖片加載方法不對

[UIImage imageNamed:]的內存緩存特性:
系統(tǒng)級緩存無法自動釋放
特別不適合大圖和列表展示場景
會自動緩存圖片到系統(tǒng)緩存
適合重復使用的小圖標
不適合大圖或單次使用的圖片
2.內存增長原因:
為了支持 GIF/WebP 等動圖格式,showImageView為SDAnimatedImageView,它解碼后的幀緩存會增加內存占用。
大圖被緩存且無法及時釋放
圖片解碼后的位圖數(shù)據(jù)占用內存
3.Cell 復用機制
快速滑動時可能同時加載多張大圖,舊圖片未及時釋放
優(yōu)化方案:
通過以下優(yōu)化措施,圖片展示內存問題應該能得到顯著改善。核心要點是:
避免使用 imageNamed: 加載大圖
合理配置 SDAnimatedImageView
完善 cell 復用機制
使用圖片下采樣技術
滑動時優(yōu)化資源使用
1.使用正確的圖片加載方式,用 [UIImage imageWithContentsOfFile:filePath]替代[UIImage imageNamed:filePath]
優(yōu)點:
不會緩存圖片
適合大圖和單次使用的圖片
后臺線程解碼 + 尺寸適配
// 后臺線程處理圖片
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
@autoreleasepool {
// 1. 從文件加載
UIImage *originalImage = [UIImage imageWithContentsOfFile:filePath];
// 2. 壓縮圖片尺寸 (按需)
CGSize targetSize = CGSizeMake(800, 800); // 根據(jù)需求調整
UIGraphicsBeginImageContextWithOptions(targetSize, NO, [UIScreen mainScreen].scale);
[originalImage drawInRect:CGRectMake(0, 0, targetSize.width, targetSize.height)];
UIImage *scaledImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
// 3. 主線程更新UI
dispatch_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:nil
completed:^(UIImage *image, NSError *error,
SDImageCacheType cacheType,
NSURL *imageURL) {
// 加載完成回調
}];
4.優(yōu)化 SDAnimatedImageView 配置
- (SDAnimatedImageView *)showImageView {
if (!_showImageView) {
_showImageView = [[SDAnimatedImageView alloc] init];
_showImageView.contentMode = UIViewContentModeScaleAspectFill;
_showImageView.userInteractionEnabled = YES;
// 添加以下優(yōu)化配置
_showImageView.shouldIncrementalLoad = YES; // 漸進式加載
_showImageView.maxBufferSize = 1024 * 1024; // 設置合理的緩沖區(qū)大小
_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.圖片尺寸優(yōu)化(針對大圖)
// 使用 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.滑動性能優(yōu)化
// 在 scrollView 代理中實現(xiàn)以下方法
- (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];
}
}
}
其他優(yōu)化建議:
1.內存警告處理
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// 清除所有圖片緩存
[[SDImageCache sharedImageCache] clearMemory];
}
2.使用合適的 SDWebImage 選項:
[self.showImageView sd_setImageWithURL:imageURL
placeholderImage:nil
options:SDWebImageAvoidDecodeImage |
SDWebImageScaleDownLargeImages |
SDWebImageProgressiveLoad
completed:nil];
3.監(jiān)控內存使用:
- (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 全局參數(shù)(AppDelegate 中)
// 設置全局緩存策略
SDImageCacheConfig *cacheConfig = [SDImageCacheConfig defaultCacheConfig];
cacheConfig.maxMemoryCost = 100 * 1024 * 1024; // 100MB 內存緩存
cacheConfig.maxMemoryCount = 50; // 最大緩存圖片數(shù)量
cacheConfig.shouldDecompressImages = NO; // 禁止自動解壓
[SDImageCache sharedImageCache].config = cacheConfig;
