20

Ứng dụng iOS của tôi có mức sử dụng bộ nhớ cao nhưng không bị rò rỉ bộ nhớ. Làm cách nào để giảm mức sử dụng bộ nhớ.Giảm mức sử dụng bộ nhớ trong ứng dụng iOS mà không bị rò rỉ

Sử dụng thiết bị, tôi phát hiện ra rằng ứng dụng của tôi tối đa là 90MB, trước khi cảnh báo bộ nhớ xảy ra và bộ nhớ khác được phân phối lại, sau đó vẫn ở mức 55-65MB cho phần còn lại của việc sử dụng.

Tôi cảm thấy rằng 55-65MB quá cao, phải không?

Vì, Dụng cụ không phát hiện bất kỳ rò rỉ nào. Tôi có thể làm gì để giảm mức sử dụng bộ nhớ này?

Tôi đã xem qua video WWDC của năm nay, nhưng về nội dung tôi đã hiểu (đây là ứng dụng iOS đầu tiên của tôi), nó chủ yếu đề cập đến việc rò rỉ.

Một số thông tin có thể hữu ích:

VM: ImageIO_GIF_Data 30.35MB Sống Bytes | 115 Sống | 300 Tạm thời | 136.12 MB Tổng số byte

VM: MappedFile 36.04 MB Live Bytes | 16 Sống | 11 Tạm thời | 36,09 MB Nhìn chung Bytes

Tất cả những thứ khác là dưới 1MB

Ứng dụng của tôi tải khoảng 30 file GIF từ internet, tôi sử dụng SDWebImage, và tôi chỉ cần lưu các URL của hình ảnh, và SDWebImage phần còn lại. : P

Cảm ơn trước,

Từ An iOS Memory Management đầu tiên hẹn giờ


Here is a screenshot of what Instruments shows me

Cảm ơn một lần nữa cho bạn giúp

+14

Nếu bạn downvote câu hỏi, xin vui lòng cho tôi biết tại sao để tôi có thể cải thiện các câu hỏi hoặc không gửi một câu hỏi như thế này một lần nữa. Cảm ơn bạn ! – GangstaGraham

+0

Bạn nói bạn đã sử dụng Nhạc cụ. Công cụ phân bổ nói gì là người sử dụng bộ nhớ chính của bạn? Xem "Khôi phục bộ nhớ bạn đã bỏ rơi" cho một số phần giới thiệu: http://developer.apple.com/library/mac/#documentation/developertools/Conceptual/InstrumentsUserGuide/MemoryManagementforYouriOSApp/MemoryManagementforYouriOSApp.html –

+0

Bạn có nhớ đăng ảnh chụp màn hình ứng dụng của mình không chạy phân bổ trong các công cụ, với "Thống kê" được chọn (mà nên là mặc định), và được sắp xếp theo Live byte với cao nhất ở đầu trang? – jaredsinclair

Trả lời

1

Tôi quyết định thêm mã đầy đủ để tiết kiệm bộ nhớ, nếu bạn đang sử dụng tệp GIF, hãy sửa đổi phương thức tỷ lệ UIImage (Tìm thấy nó ở đây, Stackoverflow). Như đã nói GangstaGraham trong SD Hình ảnh tồn tại phương pháp sd_animatedImageByScalingAndCroppingToSize

@interface UIImage (Scaling) 

-(UIImage *)imageByScalingProportionallyToSize:(CGSize)targetSize; 
-(UIImage*) croppedImageWithRect: (CGRect) rect; 

@end 

@implementation UIImage (Scaling) 

- (UIImage *)imageByScalingProportionallyToSize:(CGSize)targetSize { 

    if ([[UIScreen mainScreen] respondsToSelector:@selector(scale)]) { 
     if ([[UIScreen mainScreen] scale] == 2.0) { 
      targetSize.height *= 2.0f; 
      targetSize.width *= 2.0f; 
     } 
    } 

    NSUInteger width = targetSize.width; 
    NSUInteger height = targetSize.height; 
    UIImage *newImage = [self resizedImageWithMinimumSize: CGSizeMake (width, height)]; 
    return [newImage croppedImageWithRect: CGRectMake ((newImage.size.width - width)/2, (newImage.size.height - height)/2, width, height)]; 
} 

-(CGImageRef)CGImageWithCorrectOrientation 
{ 
    if (self.imageOrientation == UIImageOrientationDown) { 
     //retaining because caller expects to own the reference 
     CGImageRetain([self CGImage]); 
     return [self CGImage]; 
    } 

    UIGraphicsBeginImageContext(self.size); 

    CGContextRef context = UIGraphicsGetCurrentContext(); 

    if (self.imageOrientation == UIImageOrientationRight) { 
     CGContextRotateCTM (context, 90 * M_PI/180); 
    } else if (self.imageOrientation == UIImageOrientationLeft) { 
     CGContextRotateCTM (context, -90 * M_PI/180); 
    } else if (self.imageOrientation == UIImageOrientationUp) { 
     CGContextRotateCTM (context, 180 * M_PI/180); 
    } 

    [self drawAtPoint:CGPointMake(0, 0)]; 

    CGImageRef cgImage = CGBitmapContextCreateImage(context); 
    UIGraphicsEndImageContext(); 

    return cgImage; 
} 

-(UIImage*)resizedImageWithMinimumSize:(CGSize)size 
{ 
    CGImageRef imgRef = [self CGImageWithCorrectOrientation]; 
    CGFloat original_width = CGImageGetWidth(imgRef); 
    CGFloat original_height = CGImageGetHeight(imgRef); 
    CGFloat width_ratio = size.width/original_width; 
    CGFloat height_ratio = size.height/original_height; 
    CGFloat scale_ratio = width_ratio > height_ratio ? width_ratio : height_ratio; 
    CGImageRelease(imgRef); 
    return [self drawImageInBounds: CGRectMake(0, 0, round(original_width * scale_ratio), round(original_height * scale_ratio))]; 
} 

-(UIImage*)drawImageInBounds:(CGRect)bounds 
{ 
    UIGraphicsBeginImageContext(bounds.size); 
    [self drawInRect: bounds]; 
    UIImage *resizedImage = UIGraphicsGetImageFromCurrentImageContext(); 
    UIGraphicsEndImageContext(); 
    return resizedImage; 
} 

-(UIImage*)croppedImageWithRect:(CGRect)rect 
{ 

    UIGraphicsBeginImageContext(rect.size); 
    CGContextRef context = UIGraphicsGetCurrentContext(); 
    CGRect drawRect = CGRectMake(-rect.origin.x, -rect.origin.y, self.size.width, self.size.height); 
    CGContextClipToRect(context, CGRectMake(0, 0, rect.size.width, rect.size.height)); 
    [self drawInRect:drawRect]; 
    UIImage* subImage = UIGraphicsGetImageFromCurrentImageContext(); 
    UIGraphicsEndImageContext(); 

    return subImage; 
} 

-(UIImage *) resizableImageWithCapInsets2: (UIEdgeInsets) inset 
{ 
    if ([self respondsToSelector:@selector(resizableImageWithCapInsets:resizingMode:)]) 
    { 
     return [self resizableImageWithCapInsets:inset resizingMode:UIImageResizingModeStretch]; 
    } 
    else 
    { 
     float left = (self.size.width-2)/2;//The middle points rarely vary anyway 
     float top = (self.size.height-2)/2; 
     return [self stretchableImageWithLeftCapWidth:left topCapHeight:top]; 
    } 
} 

@end 

Và UIImageView:

#import <SDWebImage/SDImageCache.h> 

@implementation UIImageView (Scaling) 

-(void)setImageWithURL:(NSURL*)url scaleToSize:(BOOL)scale 
{ 
    if(url.absoluteString.length < 10) return; 
    if(!scale){ 
     [self setImageWithURL:url]; 
     return; 
    } 
    __block UIImageView* selfimg = self; 
    __block NSString* prevKey = SPRINTF(@"%@_%ix%i", url.absoluteString, (int)self.frame.size.width, (int)self.frame.size.height); 
    __block UIImage* prevImage = nil; 

    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); 
    dispatch_async(queue,^{ 

     prevImage = [[SDImageCache sharedImageCache] imageFromDiskCacheForKey:prevKey]; 
     if(prevImage){ 
      dispatch_async(dispatch_get_main_queue(),^{ 
       [self setImage:prevImage]; 
      }); 
     }else{ 

      [[SDWebImageDownloader sharedDownloader] downloadImageWithURL:url options:SDWebImageDownloaderFILOQueueMode progress:nil completed:^(UIImage *image, NSData *data, NSError *error, BOOL finished) { 
       if(error){ 
        [selfimg setImageWithURL:url scaleToSize:scale]; 
       }else{ 
        dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); 
        dispatch_async(queue,^{ 
         prevImage = [image imageByScalingProportionallyToSize:self.frame.size]; 
         if(finished) 
          [[SDImageCache sharedImageCache] storeImage:prevImage forKey:prevKey]; 
         dispatch_async(dispatch_get_main_queue(),^{ 
          [self setImage:prevImage]; 
         }); 
        }); 
       } 
      }]; 
     } 
    }); 

    return; 
} 

-(void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder scaleToSize:(BOOL)scale 
{ 
    [self setImage:placeholder]; 
    [self setImageWithURL:url scaleToSize:scale]; 
} 


@end 
+0

Cảm ơn sự giúp đỡ của bạn, @HotJard! – GangstaGraham

0

SDWebImage không làm phần còn lại . Bạn cần xử lý ít hình ảnh hơn trong bộ nhớ có thể: xóa UIImageView khi không hiển thị; sử dụng mẫu đối tượng tái sử dụng; và dĩ nhiên rõ ràng hình ảnh không nhìn thấy được (lưu trữ trong bộ nhớ) khi bạn đã có cảnh báo bộ nhớ, cho điều này chỉ cần sử dụng self.urImage = nil;

Vì vậy, cái nhìn tốt để tiết kiệm bộ nhớ ứng dụng;)

+0

Tôi sử dụng các tế bào tái sử dụng. GIF được cấu trúc trong chế độ xem bảng, vì vậy tôi giả định rằng khi các ô không hiển thị, lượt xem hình ảnh của chúng bị xóa (ARC xử lý điều này, phải không?) Tôi sẽ xem xét cách xóa bộ nhớ cache khi tôi nhận được cảnh báo bộ nhớ – GangstaGraham

+0

"Bộ nhớ cache được thiết kế để tự xóa khi bạn nhận được cảnh báo bộ nhớ, vì vậy bạn không cần phải lo lắng về nó." https://github.com/rs/SDWebImage/issues/255 – GangstaGraham

+0

SDWebImage rõ ràng KHÔNG ĐƯỢC SỬ DỤNG hình ảnh, nếu bạn vẫn xử lý cho nó tham chiếu, hình ảnh sẽ không bị xóa. Làm cách nào để u sử dụng các ô trong chế độ xem bảng? Nếu mỗi lần đặt setImageWithUrl thì tốt. Một vấn đề khác là sử dụng hình ảnh thu nhỏ, vì vậy nếu gif là 1024x768 và u cần 320x240, mức sử dụng bộ nhớ lên đến 10 lần. Sử dụng phương thức imageByScalingProportionallyToSize từ http://stackoverflow.com/questions/185652/how-to-scale-a-uiimageview-proportionally và lưu nó vào bộ nhớ cache – HotJard

4

Bạn nói rằng bạn đang sử dụng chế độ xem bảng. Mặc dù các tế bào được tái sử dụng tự động, điều này làm cho nó rất dễ dàng để làm cho những sai lầm và tạo ra quá nhiều đối tượng. 1 lỗi phổ biến là phân bổ các đối tượng (ví dụ UIImageView) trong phương thức cellForRowAtIndexPath, vì điều này có nghĩa là mỗi khi một ô được tái sử dụng, một UIImageView mới sẽ được thêm vào nó cũng như giữ các đối tượng cũ. Vì vậy, hãy kiểm tra kỹ những gì đang diễn ra trong phương thức cellForRowAtIndexPath của bạn.

+0

Tôi sử dụng [cell.anImageView setImageWithURL: someURL] cho tất cả các ô, nhưng điều này chỉ đặt hình ảnh, nó không phân bổ nó. Trong trường hợp ô không tồn tại 'if (! Cell) {// create cell}', tôi tạo ô xem bảng và trong phương thức khởi tạo của tôi, tôi tạo một lần xem hình ảnh. Điều này là đúng, phải không? Cảm ơn sự hỗ trợ. :) – GangstaGraham

+0

Vâng, đó là chính xác. – Can

+0

@ Darren cảm ơn lời khuyên tốt – oyatek

1

Tôi khuyên bạn nên sử dụng Công cụ và Phân tích Heapshot. bbum đã viết một bài báo về nó tại his blog.

Đây là một cái nhìn tổng quát:

  1. Bắt đầu App của bạn trong Instruments và chọn Phân bổ mẫu
  2. Chờ một thời gian sau khi App của bạn bắt đầu để ổn định cuộc sống
  3. Trong phân bổ, ấn Mark Heap; Đây là đường cơ sở của bạn.
  4. Sử dụng ứng dụng của bạn và quay lại màn hình giống như trong # 2. Nhấn lại Mark Heap.
  5. Lặp lại điều đó một thời gian.

Nếu bạn thấy bộ nhớ tăng trưởng ổn định, bạn có thể xem chi tiết trong heapshots và xem tất cả các đối tượng được phân bổ. Điều đó sẽ cung cấp cho bạn một khởi đầu tốt để giảm dấu chân bộ nhớ của bạn.

+0

Một cuộc gọi là hình ảnh được tải xuống, dung lượng bộ nhớ khá ổn định. Tôi nghĩ rằng nội dung GIF quá nặng, vì vậy mức sử dụng bộ nhớ cao, tôi đang cố gắng tìm cách giảm kích thước các tệp. @mkalmes – GangstaGraham