2015-10-22 38 views
7

Używam biblioteki wyboru obrazów, aby umożliwić użytkownikowi wybieranie wielu zdjęć z ich biblioteki zdjęć. Są one zwracane w postaci tablicy PHAssets. Następnie chcę przekonwertować wszystkie PHAssets na UIImages i zapisać je w pamięci aplikacji.Korzystanie z dużej ilości pamięci przez zestawy PHA i wywoływanie requestImageForAsset

W tej chwili przeglądam wszystkie zasoby i synchronicznie synchronizuję numer requestImageForAsset. Mój problem polega na tym, że podczas uruchamiania tej pętli występuje niesamowicie wysokie zużycie pamięci (przy 30 obrazach wzrasta do 130 MB). Chciałbym temu zapobiec.

Oto mój kod:

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++; 
} 

podstawie dzienników, widzę, że cały „obrazek zaczynając wzywającego x” są nazywane, potem wszystkie bloki ukończenia („zakończył prośbą obrazu X”). Myślę, że to może przyczyniać się do problemu z pamięcią. Prawdopodobnie byłoby mniej pamięci wymagającej intensywności, aby upewnić się, że blok zakończenia każdej iteracji jest wywoływany przed zwolnieniem tych zasobów i przejściem do kolejnej iteracji. Jak mogę to zrobić?

Odpowiedz

4

Proszę używać autoreleasepool do zarządzania pamięcią.

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

Dzięki.Próbowałem @autoreleasepool w pętli for, ale umieszczenie go zarówno tam, jak i programu obsługi wyników, sprawiło, że trik! – Charles

+1

Ta sztuczka nie działa dla mnie. : -s –

5

@Jeśli sztuczka Kumar Rathore nie działa dla mnie. więc próbowałem dowiedzieć się więcej o PHImageManager here

stwierdziliśmy, że jeśli mogę przełączyć z

- requestImageForAsset:targetSize:contentMode:options:resultHandler:

do

- requestImageDataForAsset:options:resultHandler:

otrzymam obraz o tym samym wymiarze {5376 , 2688}, ale rozmiar w bajcie jest znacznie mniejszy. Więc problem z pamięcią został rozwiązany.

nadzieję, że ta pomoc!

(uwaga: [UIImage imageWithData: imageData] W tej konwersji NSData do UIImage)

0

że rozwiązany z rekurencyjnym metody, która gwarantuje, że blok zakończenie jest zakończone przed ponownym iteracji. W ten sposób można uzyskać tysiące zdjęć z bardzo małą ilością pamięci. Oto co robi:

Biorąc pod tablicą wskaźników wybranych zdjęć w rolki aparatu

  1. Zapytanie pierwszym obiektem w tablicy.
  2. W bloku zakończenia usuń pierwszy obiekt z tablicy i ponownie wywołaj tę samą metodę.
  3. powtarzać aż tablica jest pusta

Oto kod.

- (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]; 
     } 
    }]; 
    }); 
} 

Należy sprawdzić, czy tablica nie jest pusta przed wywołaniem tej metody po raz pierwszy.

+0

Korzystając z ** @ autoreleasepool **, zużycie pamięci może zostać obniżone jeszcze bardziej. –