6
votes

Scrollez la vue et la vue de tableau des performances lors du chargement des images du disque

J'essaie d'améliorer la performance de mon application iPhone intensive d'image en utilisant un cache d'image basé sur disque au lieu de passer sur le réseau. J'ai modélisé mon cache d'image après Sdimagecache ( http://github.com/ RS / SDWEBIMAGE / BLOB / MASTER / SDIMAGECACHACHE.M ), et est à peu près la même chose mais sans cache asynchrones dans les opérations d'entrée / sortie de cache asynchrones.

J'ai des vues de défilement et des vues de table qui chargent ces images de manière asynchrone. Si l'image est sur le disque, elle est chargée à partir du cache d'image, sinon une demande de réseau est effectuée et que le résultat suivant est stocké dans le cache. P>

Le problème que je suis en cours d'exécution, c'est que je fais défiler À travers les vues de défilement ou les vues de table, il existe un décalage notable car l'image est chargée à partir du disque. En particulier, l'animation d'une page d'une page à une autre sur une vue de défilement a un petit gel au milieu de la transition. P>

J'ai essayé de résoudre ce problème par: p>

  • Utilisation d'un nsoperationQueue et de NsinVocation d'objets pour rendre les demandes d'accès au disque (de la même manière que sdimagecache), mais cela n'aide pas le galement du tout. Li>
  • modifier le code du contrôleur de la vue de défilement de sorte qu'il charge uniquement des images lorsque la vue de défilement n'est plus défilante. Cela signifie que les incendies de disque uniquement lorsque la vue de défilement cesse de défilement, mais si j'essaie immédiatement de faire défiler jusqu'à la page suivante, je peux remarquer le retard lorsque l'image se charge du disque. Li> ul>

    est là un moyen de rendre mes accès à mon disque meilleur ou d'avoir moins d'effet sur l'interface utilisateur? strong> p>

    Notez que je fais déjà la mise en cache du images dans la mémoire aussi. Donc, une fois que tout est chargé en mémoire, l'interface utilisateur est belle et réactive. Mais lorsque l'application démarre, ou si des avertissements de mémoire faible sont expédiés, je vais faire l'expérience de nombreux retards d'interface utilisateur, car les images sont chargées à partir de disque. P>

    Les extraits de code correspondants sont ci-dessous. Je ne pense pas que je fais quelque chose de fantaisie ou de fou. Le décalage ne semble pas être perceptible sur un iPhone 3G, mais il est assez apparent sur un iPod Touch 2nd-Gen. P>

    Cache de cache d'image: strong> P>

    Voici un extrait pertinent de mon code de cache d'image. Assez simple. P> xxx pré>

    Vue du défilement Code du contrôleur strong> p>

    Voici un extrait pertinent du code que j'utilise pour afficher des images dans Mes contrôleurs de vue de défilement. P>

    - (void)imageLoadedFor:(NSNumber *)index image:(UIImage *)image {
        // Cache image in memory
        // ...
    
        UIView *view = [self.views objectForKey:index];
        UIImageView *imageView = (UIImageView *) [view viewWithTag:kImageViewTag];
        imageView.contentMode = UIViewContentModeScaleAspectFill;
        imageView.image = image;
    }
    


4 commentaires

Nous avons le même problème sur iPad avec de grandes images. Je vous ferai savoir si nous trouvons une solution


L'une des touches principales du défilement performant est de ne jamais bloquer le fil principal. Le thread principal est le fil d'interface utilisateur, qui doit gérer les mises à jour élevées de framerate à l'écran et les rappels événementiels associés à votre contrôleur. Sans voir votre code NsuurlConnection, je ne peux pas dire si vous le faites dans un fil séparé ou non, ce serait ce que ce soit mon premier chasseur. Pouvez-vous publier votre code de traitement du réseau?


Vérifiez le projet TCIMageView GitHub. Ils ont une jolie mise en œuvre soignée pour les images de chargement paresseux et la cache-disque github.com/totocaster /TcImageView/blob/master/tcimageview.m


Checkout Uiimageloader - Github.com/gngrwzrd/uiimageloader


5 Réponses :


0
votes

L'image du disque est en réalité lue lors du dessin de l'image sur l'imageView. Même si nous mettons en cache la lecture de l'image du disque, cela n'affecte pas car il ne conserve que la référence au fichier. Vous devrez peut-être utiliser un carrelage d'images plus grandes à cette fin.

Cordialement, Deepa


0 commentaires

1
votes

Vous devriez consulter l'application de l'exemple LazyTableImages.


0 commentaires

1
votes

Si vous avez réduit l'activité de réseau, j'essaierais d'encapsuler votre demande pour vous assurer qu'elle est à 100% du fil principal. Bien que vous puissiez utiliser NsurlConnection de manière asynchrone et répondre à ses méthodes de déléguement, je trouve plus facile d'envelopper une demande synchrone dans une opération d'arrière-plan. Vous pouvez utiliser Nsoperation ou une expédition centrale Grand si vos besoins sont plus complexes. Un exemple (relativement) simple dans une implémentation d'ImageLoader pourrait être:

// imageLoader.m

// assumes that that imageCache uses kvp to look for images
- (UIImage *)imageForKey:(NSString *)key
{
    // check if we already have the image in memory
     UImage *image = [_images objectForKey:key];

    // if we don't have an image:
    // 1) start a background task to load an image from a file or URL
    // 2) return a default image to display while loading
    if (!image) {
        [self performSelectorInBackground:@selector(loadImageForKey) withObject:key];
        image = [self defaultImage];
    }

    return image;
}

- (void)loadImageForKey:(NSString *)key
{
    NSAutoReleasePool *pool = [[NSAutoReleasePool alloc] init];

    // attempt to load the image from the file cache
    UIImage *image = [self imageFromFileForKey:key];

    // if no image, load the image from the URL
    if (!image) {
        image = [self imageFromURLForKey:key];
    }

    // if no image, return default or imageNotFound image
    if (!image) {
        image = [self notFoundImage];
    }

    if ([_delegate respondsTo:@selector(imageLoader:didLoadImage:ForKey:)]) {
        [_delegate imageLoader:self didLoadImage:image forKey:key];
    }

    [pool release];
}

- (UIImage *)imageFromURLForKey:(NSString *)key
{
    NSError *error = nil;
    NSData *imageData = [NSData dataWithContentsOfURL:[self imageURLForKey:key]
                                              options:0
                                                error:&error];

    UIImage *image;

    // handle error if necessary
    if (error) {
        image = [self errorImage];
    }

    // create image from data
    else {
        image = [UIImage imageWithData:imageData];
    }

    return image;
}


0 commentaires

0
votes

J'ai eu ce problème - vous frappez la limite de la rapidité avec laquelle l'interface utilisateur peut charger une image lors du défilement - je vais donc contourner le problème et améliorer l'expérience utilisateur.

  1. Ne chargez que des images lorsque le rouleau se trouve à une nouvelle «page» (rectangle statique) et
  2. Mettez un indicateur d'activité derrière une vue de défilement transparente pour gérer le cas où l'utilisateur défile plus rapidement que l'application peut charger le contenu

0 commentaires

0
votes

C'est généralement le décodage de l'image qui prend du temps, ce qui provoque une gel des interfaces internes (puisque tout se passe sur le fil principal). Chaque fois que vous appelez [UIIMAGE ImageWithData:] sur une grande image, vous remarquerez un hoquet. Décodage Une image plus petite est beaucoup plus rapide.

Deux options:

  1. Vous pouvez d'abord charger une version miniature de chaque image, puis "Aiguisir" sur Didfinishscrolling . Les vignettes doivent décoder rapidement suffisamment de telle sorte que vous ignorez de cadres.
  2. Vous pouvez charger une version miniature de chaque image ou afficher un indicateur de chargement en premier, puis décodez l'image de résolution complète sur un autre fil et sous le subtiliser quand il est prêt. (C'est la technique qui est utilisée dans l'application de photos indigènes).

    Je préfère la deuxième approche; C'est assez facile avec GDC de nos jours.


0 commentaires