减少iOS应用程序中的内存使用,而无泄漏

减少iOS应用程序中的内存使用,而无泄漏,ios,objective-c,cocoa-touch,automatic-ref-counting,instruments,Ios,Objective C,Cocoa Touch,Automatic Ref Counting,Instruments,我的iOS应用程序内存使用率高,但没有内存泄漏。如何降低内存使用率 通过使用仪器,我发现我的应用程序在内存警告出现之前最大容量为90MB,其他内存被释放,然后在剩余的使用时间里它保持在55-65MB左右 我觉得55-65MB太高了,对吗? 因为,仪器没有发现任何泄漏。我可以做些什么来减少内存使用 我看了今年的WWDC视频,但我了解的东西(这是我的第一个iOS应用程序)大部分都涉及处理泄密 一些可能有用的信息: VM:ImageIO_GIF_数据30.35MB活动字节| 115活动| 300瞬态|

我的iOS应用程序内存使用率高,但没有内存泄漏。如何降低内存使用率

通过使用仪器,我发现我的应用程序在内存警告出现之前最大容量为90MB,其他内存被释放,然后在剩余的使用时间里它保持在55-65MB左右

我觉得55-65MB太高了,对吗?

因为,仪器没有发现任何泄漏。我可以做些什么来减少内存使用

我看了今年的WWDC视频,但我了解的东西(这是我的第一个iOS应用程序)大部分都涉及处理泄密

一些可能有用的信息:

VM:ImageIO_GIF_数据30.35MB活动字节| 115活动| 300瞬态| 总字节数为136.12 MB

VM:MappedFile36.04 MB活动字节| 16个活动字节| 11个临时字节| 36.09 MB总字节

所有其他内容都低于1MB

我的应用程序从互联网下载大约30个GIF文件,我使用SDWebImage,我只保存图像的URL,其余的由SDWebImage完成P

提前感谢,

从iOS内存管理第一个计时器



再次感谢您的帮助

SDWebImage不做其他事情。 您需要在内存中处理尽可能少的图像: 当UIImageView未显示时,将其删除; 使用可重用对象模式; 当然,当出现内存警告时,清除不可见(缓存在内存中)图像, 为此,只需使用
self.urImage=nil


因此,节省应用程序内存的外观不错;)

您说您正在使用表视图。虽然单元会自动重用,但这很容易出错并创建过多的对象。
一个常见错误是在CellForRowatineXpath方法中分配对象(例如UIImageView),因为这意味着每次重用单元格时,都会向其中添加一个新的UIImageView,并保留旧的UIImageView。因此,请仔细检查您的cellForRowAtIndexPath方法中发生的情况。

我建议您使用仪器和Heapshot分析。bbum在上写了一篇关于它的文章

下面是一个快速概述:

  • 在Instruments中启动应用程序并选择分配模板
  • 在应用程序开始稳定后等待一段时间
  • 在分配中,按
    标记堆
    ;这是你的底线
  • 使用你的应用程序并返回到与#2中相同的屏幕。再次按
    标记堆
  • 重复一段时间

  • 如果您看到内存稳步增长,您可以在堆照片中深入查看所有分配的对象。这将为您减少内存占用提供一个良好的开端。

    我决定添加完整的代码以节省内存,如果您使用的是GIF文件,请修改UIImage scale方法(在此处发现,这是堆栈溢出)。如前所述,在SD图像中,存在方法SD_通过缩放和裁剪来制作动画图像

    @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
    
    和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
    
    #导入
    @实现UIImageView(缩放)
    -(void)setImageWithURL:(NSURL*)url比例大小:(BOOL)比例
    {
    if(url.absoluteString.length<10)返回;
    如果(!比例){
    [self-setImageWithURL:url];
    返回;
    }
    __块UIImageView*selfimg=self;
    __block NSString*prevKey=SPRINTF(@“%@%ix%i”,url.absoluteString,(int)self.frame.size.width,(int)self.frame.size.height);
    __块UIImage*prevImage=nil;
    调度队列=调度获取全局队列(调度队列优先级默认为0);
    调度异步(队列,^{
    prevImage=[[SDImageCache sharedImageCache]imageFromDiskCacheForKey:prevKey];
    如果(前置图像){
    dispatch\u async(dispatch\u get\u main\u queue(),^{
    [自我设置图像:prevImage];
    });
    }否则{
    [[SDWebImageDownloader sharedDownloader]DownloadeImageWithURL:url选项:SDWebImageDownloaderFILOQueueMode进度:无完成:^(UIImage*图像,NSData*数据,NSError*错误,BOOL完成){
    如果(错误){
    [selfimg setImageWithURL:url比例大小:比例];
    }否则{
    调度队列=调度获取全局队列(调度队列优先级默认为0);
    调度异步(队列,^{
    prevImage=[image ImageByScalingProgressionlyToSize:self.frame.size];
    如果(完成)
    [[SDImageCache sharedImageCache]storeImage:prevImage forKey:prevKey];
    dispatch\u async(dispatch\u get\u main\u queue(),^{
    [自我设置图像:prevImage];
    });
    });
    }
    }];
    }
    });
    返回;
    }
    -(void)setImageWithURL:(NSURL*)url占位符图像:(UIImage*)占位符比例大小:(BOOL)比例
    {
    [自我设置图像:占位符];
    [self-setImageWithURL:url比例大小:比例];
    }
    @结束
    
    我确实使用可重复使用的单元。GIF是在表视图中构造的,因此我假设当单元格不可见时,它们的图像视图将被删除(ARC处理这个问题,对吗?),当我收到内存警告时,我将研究清除缓存的方法“缓存设计为在收到内存警告时自动刷新,因此您不必担心。”SDWebImage清除未使用的图像,如果仍在处理对它的引用,则不会清除图像。如何在表格视图中使用单元格?如果每次设置setImageWithUrl就好了。另一个问题是使用缩放图像,所以如果gif是1024x768,而您需要320x240,内存使用率将高达10倍。使用ImageByScaling PrototallyToSize方法并将其保存到缓存我确实使用了setImageWithUrl,但如果我使用它,如何将其与ImageByScalingPrototallyToSize集成,因为它不会已经设置图像,然后我必须使用ImageByScalingPrototallyToSize?谢谢你的帮助,我很感激,我只需要理解它,谢谢。:)@Hotjard事实上我发现如果我把它添加到