2015-10-22 9 views
7

Sto utilizzando una libreria di selezione immagini per consentire all'utente di selezionare molte immagini dalla sua libreria di foto. Vengono restituiti come una serie di PHAssets. Quindi, voglio convertire tutti gli PHAssets in UIImages e scriverli nella memoria dell'app.Utilizzo memoria elevato che passa attraverso PHAssets e richiesta di chiamataImageForAsset

Al momento, sto collegando tutte le risorse e chiamando lo requestImageForAsset in modo sincrono. Il mio problema è che c'è un picco di utilizzo della memoria incredibilmente alto quando viene eseguito questo ciclo (con 30 immagini, raggiunge i 130 MB). Vorrei impedire questo.

Ecco il mio codice:

for(PHAsset *asset in self.assets) { 
     NSLog(@"started requesting image %i", i); 
     [[PHImageManager defaultManager] requestImageForAsset:asset targetSize:PHImageManagerMaximumSize contentMode:PHImageContentModeAspectFit options:[self imageRequestOptions] resultHandler:^(UIImage *image, NSDictionary *info) { 
      dispatch_async(dispatch_get_main_queue(), ^{ 
       assetCount++; 
       NSError *error = [info objectForKey:PHImageErrorKey]; 
       if (error) NSLog(@"Image request error: %@",error); 
       else { 
        NSString *imagePath = [appDelegate.docsPath stringByAppendingPathComponent:[NSString stringWithFormat:@"%i.png",i]]; 
        NSData *imageData = UIImagePNGRepresentation(image); 
        if(imageData) { 
         [imageData writeToFile:imagePath atomically:YES]; 
         [self.imagesArray addObject:imagePath]; 
        } 
        else { 
         NSLog(@"Couldn't write image data to file."); 
        } 
        [self checkAddComplete]; 
        NSLog(@"finished requesting image %i", i); 
       } 
      }); 
     }]; 
    i++; 
} 

Sulla base dei tronchi, vedo che tutti i "immagine di partenza chiedendo x" sono chiamati in primo luogo, poi tutti i blocchi di completamento ("finito richiedendo immagine x"). Penso che questo potrebbe contribuire al problema della memoria. Probabilmente richiederebbe meno memoria per garantire che il blocco di completamento per ogni iterazione venga chiamato prima di liberare tali risorse e passare alla successiva iterazione. Come posso fare questo?

risposta

4

Utilizzare autoreleasepool per la gestione della memoria.

for(PHAsset *asset in self.assets) { 
    // This autorelease pool seems good (a1) 
    @autoreleasepool { 
     NSLog(@"started requesting image %i", i); 
     [[PHImageManager defaultManager] requestImageForAsset:asset targetSize:PHImageManagerMaximumSize contentMode:PHImageContentModeAspectFit options:[self imageRequestOptions] resultHandler:^(UIImage *image, NSDictionary *info) { 
      dispatch_async(dispatch_get_main_queue(), ^{ 
       //you can add autorelease pool here as well (a2) 
       @autoreleasepool { 
        assetCount++; 
        NSError *error = [info objectForKey:PHImageErrorKey]; 
        if (error) NSLog(@"Image request error: %@",error); 
        else { 
         NSString *imagePath = [appDelegate.docsPath stringByAppendingPathComponent:[NSString stringWithFormat:@"%i.png",i]]; 
         NSData *imageData = UIImagePNGRepresentation(image); 
         if(imageData) { 
          [imageData writeToFile:imagePath atomically:YES]; 
          [self.imagesArray addObject:imagePath]; 
         } 
         else { 
          NSLog(@"Couldn't write image data to file."); 
         } 
         [self checkAddComplete]; 
         NSLog(@"finished requesting image %i", i); 
        } 
       } //a2 ends here 
      }); 
     }]; 
     i++; 
    } // a1 ends here 
} 
+1

Grazie.Ho provato @autoreleasepool nel ciclo for, ma inserendolo in entrambi e il gestore del risultato ha fatto il trucco! – Charles

+1

Questo trucco non funziona per me. : -s –

5

@Inder Kumar Trucco Rathore non funziona per me. Così ho provato Per saperne di più PHImageManager here

ho scoperto che se passo da

- requestImageForAsset:targetSize:contentMode:options:resultHandler:

a

- requestImageDataForAsset:options:resultHandler:

io ricevere l'immagine con la stessa dimensione {5376 , 2688} ma la dimensione in byte è molto più piccola. Quindi il problema della memoria è risolto.

spero che questo aiuto !!

(nota: [UIImage imageWithData: imageData] utilizzare questo per convertire NSData a UIImage)

0

ho risolto con un metodo ricorsivo che garantisce che il blocco completamento viene completato prima iterazione successiva. In questo modo è possibile ottenere migliaia di foto con poca memoria. Ecco ciò che fa:

Dato un array di indici di foto selezionate in rullino

  1. Request primo oggetto matrice.
  2. Nel blocco di completamento, rimuovere il primo oggetto nell'array e chiamare di nuovo lo stesso metodo.
  3. Ripetere fino array è vuoto

Ecco il codice.

- (void)processPhotos 
{ 
    NSIndexPath *indexPath = [_selectedPhotosArray objectAtIndex:0]; 
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 
    PHAsset *asset = [_allPhotos objectAtIndex:indexPath.row]; 
    [self.imageManager requestImageForAsset:asset 
           targetSize:PHImageManagerMaximumSize 
           contentMode:PHImageContentModeAspectFill 
            options:self.imageRequestOptions 
           resultHandler:^(UIImage * _Nullable result, NSDictionary * _Nullable info) { 
     NSError *error = [info objectForKey:PHImageErrorKey]; 
     if (_selectedPhotosArray.count > 0) { 
     [_selectedPhotosArray removeObjectAtIndex:0]; 
     } 
     if (error) { 
     NSLog(@"[CameraRoll] Image request error: %@",error); 
     } else { 
     if (result != nil) { 
      [self processImage:result]; 
     } 
     } 
     if (_selectedPhotosArray.count > 0) { 
     // Recurring loop 
     [self processPhotos]; 
     } 
    }]; 
    }); 
} 

Assicurarsi di verificare se la matrice non è vuota prima di chiamare questo metodo per la prima volta.

+0

Usando ** @ autoreleasepool ** con questo, l'utilizzo della memoria può essere abbassato ancora di più .. –

Problemi correlati