web-dev-qa-db-fra.com

Réduire l'utilisation de la mémoire dans l'application iOS sans fonctions

Mon application iOS utilise beaucoup de mémoire, mais aucune fuite de mémoire. Comment puis-je réduire l'utilisation de la mémoire.

À l'aide d'Instruments, j'ai découvert que mon application atteignait 90 Mo maximum, avant qu'un avertissement en mémoire ne se produise, que d'autres mémoires soient libérées et qu'elles restent autour de 55 à 65 Mo pour le reste de leur utilisation.

Je pense que 55-65 Mo est trop élevé, non?

_ {Depuis, Instruments n'a détecté aucune fuite. Que puis-je faire pour réduire cette utilisation de la mémoire?} _

J'ai visionné la vidéo de la WWDC de cette année, mais ce que j'ai compris (il s'agit de ma première application iOS) était principalement consacrée aux fuites.

Quelques informations éventuellement utiles:

VM: ImageIO_GIF_Data 30.35MB Live Octets | 115 vie | 300 transitoire | 136,12 Mo d'octets globaux

VM: MappedFile 36.04 MB Live Octets | 16 vivre | 11 transitoire | 36,09 Mo total d'octets

Toutes les autres choses sont sous 1MB

Mon application télécharge environ 30 fichiers GIF sur Internet, j'utilise SDWebImage, je viens d'enregistrer les URL des images et SDWebImage se charge du reste. : P

Merci d'avance,

D'un premier minuteur de gestion de mémoire iOS


 Here is a screenshot of what Instruments shows me

Merci encore pour votre aide

20
GangstaGraham

J'ai décidé d'ajouter le code complet pour la sauvegarde de la mémoire. Si vous utilisez des fichiers GIF, modifiez la méthode d'échelle UIImage (trouvé ici, un Stackoverflow). Comme dit GangstaGraham in SD Image méthode existe 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

Et 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
1
HotJard

Vous dites que vous utilisez une vue sous forme de tableau. Bien que les cellules soient réutilisées automatiquement, il est très facile de faire des erreurs et de créer trop d'objets. Une erreur commune est l'allocation d'objets (par exemple, UIImageView) dans la méthode cellForRowAtIndexPath, car cela signifie que chaque fois qu'une cellule est réutilisée, un nouvel UIImageView est ajouté à celle-ci, tout en conservant les anciens. Vérifiez donc ce qui se passe dans votre méthode cellForRowAtIndexPath.

4
Darren

Je suggérerais que vous utilisiez Instruments et Heapshot Analysis. bbum a écrit un article à ce sujet sur son blog .

Voici un aperçu rapide:

  1. Lancez votre application dans Instruments et sélectionnez le modèle d’allocations.
  2. Attendez que votre application commence à se calmer
  3. Dans Allocations, appuyez sur Mark Heap; Ceci est votre base.
  4. Utilisez votre application et revenez au même écran que dans # 2. Appuyez à nouveau sur Mark Heap.
  5. Répétez cela pendant un certain temps.

Si vous constatez une croissance régulière de la mémoire, vous pouvez explorer les photos de pile et voir tous les objets alloués. Cela devrait vous donner un bon départ pour réduire votre empreinte mémoire.

1
mkalmes

SDWebImage ne fait pas le reste. Vous avez besoin de moins d’images en mémoire que ce qui est possible: effacez UIImageView quand il n’est pas affiché; utiliser un motif d'objets réutilisables; et bien sûr des images claires non visibles (mises en cache dans la mémoire) lorsque vous avez des avertissements de mémoire, utilisez simplement self.urImage = nil;

Alors, regardez bien pour économiser de la mémoire d'application;)

0
HotJard