iOS即時通訊發(fā)送圖片消息內存暴漲優(yōu)化

問題:

即時通訊App在發(fā)送圖片消息時內存暴漲導致網(wǎng)絡請求初始化失?。▋却娌蛔鉕OM),發(fā)送消息失敗。

可能原因分析:

  1. 有內存泄漏。
  2. 發(fā)送的圖片消息,可能包含大圖,沒有進行壓縮處理,導致內存占用過高。
  3. 在發(fā)送過程中,可能同時進行了圖片的讀取和處理,如果圖片很大,處理過程中會產(chǎn)生很大的內存峰值。

解決方案:

  1. 對于發(fā)送消息時的內存暴漲:
  • 檢查是否有內存泄漏,使用Instruments工具檢測。
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.圖片加載方法不對

Cell圖片展示

[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;
性能對比
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容