2011-02-07 8 views
14

Sto implementando un'app per iOS che deve recuperare una grande quantità di immagini su HTTP. Ho provato diversi approcci, ma indipendentemente da ciò che faccio, Instuments mostra costantemente l'aumento delle allocazioni di memoria e l'App si blocca prima o poi quando la eseguo su un dispositivo. Non ci sono perdite mostrate dagli strumenti.Allocazione di memoria in costante aumento durante il recupero di immagini su HTTP in iOS

Finora ho provato i seguenti approches:

  • recuperano le immagini utilizzando un NSURLConnection sincrona all'interno di un NSOperation
  • Fetch le immagini utilizzando un NSURLConnection asincrona in un NSOperation
  • Fetch le immagini utilizzando [ NSData dataWithContentsOfURL: url] nella discussione principale
  • Scarica le immagini utilizzando ASIHTTPRequest sincrono all'interno di una NSOperazione
  • Scarica l'immagine immagini utilizzando ASIHTTPRequest asincrono e aggiungendolo a un NSOperationQueue
  • Fetch le immagini utilizzando asincrona ASIHTTPRequest e utilizzando un completionBlock

L'albero delle chiamate in Instrumetns dimostra che la memoria si consuma durante l'elaborazione HTTP-risposta. In caso di NSURLConnection asincrono è in

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data { 
[receivedData appendData:data]; 
} 

nel caso del NSURLConnection sincrono, Instruments mostra un CFData (deposito) voce crescente. Il problema con ASIHTTPRequest sembra essere lo stesso con il NSURLConnection asincrono in una posizione di codice analoga. L'approccio [NSData dataWithContentsOfURL: url] mostra una quantità crescente di allocazione di memoria totale in questa affermazione.

Sto utilizzando un NSAutoReleasePool quando la richiesta viene eseguita in un thread separato e ho provato a liberare memoria con [[NSURLCache sharedURLCache] removeAllCachedResponses] - nessun successo.

Eventuali idee/suggerimenti per risolvere il problema? Grazie.

Modifica: Il comportamento si presenta solo se persisto le immagini utilizzando CoreData. Ecco il codice corro come un NSInvocationOperation:

-(void) _fetchAndSave:(NSString*) imageId { 
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 
NSString *url = [NSString stringWithFormat:@"%@%@", kImageUrl, imageId]; 
HTTPResponse *response = [SimpleHTTPClient GET:url headerOrNil:nil]; 
NSData *data = [response payload]; 

if(data && [data length] > 0) { 
    UIImage *thumbnailImage = [UIImage imageWithData:data]; 
    NSData *thumbnailData = UIImageJPEGRepresentation([thumbnailImage scaleToSize:CGSizeMake(55, 53)], 0.5); // UIImagePNGRepresentation(thumbnail); 

    [self performSelectorOnMainThread:@selector(_save:) withObject:[NSArray arrayWithObjects:imageId, data, thumbnailData, nil] waitUntilDone:NO]; 
} 
[pool release]; 
} 

Tutti CoreData cose relative avviene nel Main-thread qui, quindi non ci dovrebbe essere alcun problema multithreading CoreData. Tuttavia, se persisto le immagini, Instruments mostra allocazioni di memoria costantemente crescenti nelle posizioni sopra descritte.

Edit II:

CoreData codice relativo:

-(void) _save:(NSArray*)args { 
NSString *imageId = [args objectAtIndex:0]; 
NSData *data = [args objectAtIndex:1]; 
NSData *thumbnailData = [args objectAtIndex:2]; 

Image *image = (Image*)[[CoreDataHelper sharedSingleton] createObject:@Image]; 
image.timestamp = [NSNumber numberWithDouble:[[NSDate date] timeIntervalSince1970]]; 
image.data = data; 

Thumbnail *thumbnail = (Thumbnail*)[[CoreDataHelper sharedSingleton] createObject:@"Thumbnail"]; 
thumbnail.data = thumbnailData; 
thumbnail.timestamp = image.timestamp; 
[[CoreDataHelper sharedSingleton] save]; 
} 

Da CoreDataHelper (self.managedObjectContext è scegliere il NSManagedObjectContext utilizzabile nel thread corrente):

-(NSManagedObject *) createObject:(NSString *) entityName { 
return [NSEntityDescription insertNewObjectForEntityForName:entityName inManagedObjectContext:self.managedObjectContext]; 
} 
+0

È possibile riprodurre il problema con un [piccolo esempio di codice] (http://sscce.org/) e postarlo? – zoul

+0

Non potevo! Anche se Instruments mostra una memoria crescente nelle posizioni menzionate, il comportamento si mostra solo se memorizzo le immagini usando CoreData. – sink12

+0

Hai trovato la ragione per la crescita costante della memoria? –

risposta

0

In questo caso, stai vedendo esattamente quello che dovresti vedere. -[NSMutableData appendData:] aumenta la dimensione del suo buffer interno per contenere i nuovi dati.Poiché uno NSMutableData si trova sempre in memoria, ciò provoca un aumento corrispondente dell'utilizzo della memoria. Cosa ti aspettavi?

Se la destinazione finale di queste immagini è su disco, provare a utilizzare NSOutputStream anziché NSMutableData. Se si desidera visualizzare l'immagine, è possibile creare un UIImage che punta al file quando hai finito.

+0

Anche dopo aver rilasciato l'oggetto NSMutableData, la memoria non viene liberata. – sink12

+0

Qualcun altro potrebbe avere un riferimento all'istanza 'NSMutableData'. Non posso dire dal codice che hai postato. Inoltre, non vi è alcuna garanzia che il sistema operativo contrassegnerà la memoria come liberata non appena ne avrete finito. In molti casi, rimarrà allocata in modo che possa essere riutilizzata (piuttosto che 'malloc()' deve chiamare 'sbrk()' che può essere piuttosto lento.) –

9

Abbiamo avuto un problema simile. Durante il recupero di molte immagini su http, c'era un'enorme crescita e un pattern a dente di sega nell'assegnazione della memoria. Vedremmo il sistema ripulire, più o meno, mentre andava, ma lentamente, e non in modo prevedibile. Nel frattempo i download erano in streaming, accumulando tutto ciò che tratteneva nella memoria. L'allocazione della memoria cresterebbe intorno ai 200 milioni e poi moriremmo.

Il problema era un problema NSURLCache. Hai dichiarato di aver provato [[NSURLCache sharedURLCache] removeAllCachedResponses]. Abbiamo provato anche quello, ma poi abbiamo provato qualcosa di diverso.

nostri download sono fatte in gruppi di N/filmati, in cui N era tipicamente da 50 a 500. E 'stato importante che otteniamo tutti N come un'operazione atomica.

Prima abbiamo iniziato il nostro gruppo di download HTTP, abbiamo fatto questo:

NSURLCache *sharedCache = [[NSURLCache alloc] initWithMemoryCapacity:0 diskCapacity:0 diskPath:0]; 
[NSURLCache setSharedURLCache:sharedCache]; 

Abbiamo poi ottenere ogni immagine in N su HTTP con una chiamata sincrona. Facciamo questo download di gruppo in una NSOperation, quindi non stiamo bloccando l'interfaccia utente.

NSData *movieReferenceData = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error]; 

Infine, dopo ogni immagine individuale download, e dopo abbiamo finito con il nostro oggetto NSData per quell'immagine, che noi chiamiamo:

[sharedCache removeAllCachedResponses]; 

Il nostro comportamento picco allocazione di memoria è sceso a una manciata molto confortevole di megabyte e ha smesso di crescere.

+0

ho fatto lo stesso ma il mio cfdata (archivio) mantiene in crescita. Sto facendo questo nel thread bg btw (se fa qualche diff) –

+0

Stai copiando i byte o usando CFDataCreateWithBytesNoCopy? Forse stai appendendo una copia che non ti aspettavi. (https://developer.apple.com/library/mac/#documentation/CoreFOundation/Conceptual/CFBinaryData/Tasks/CFWorkingBinaryData.html#//apple_ref/doc/uid/20002120-CJBDBHCB) –

+0

no ..... malato provalo e guarda cosa succede –

Problemi correlati