Wypełnianie NSImage danymi z asynchronicznego połączenia NSUR

Uderzyłem w przysłowiową ścianę, próbując dowiedzieć się, jak wypełnić NSImage danymi zwróconymi z asynchronicznego połączenia NSURLC w mojej aplikacji komputerowej (NIE aplikacji na iPhone'a !!).

Oto sytuacja.

Mam tabelę, która używa niestandardowych komórek. W każdej niestandardowej komórce znajduje się obraz NSImage, który jest pobierany z serwera WWW. Aby wypełnić obraz, mogę łatwo wykonać synchroniczne żądanie:

myThumbnail = [[NSImage alloc] initWithContentsOfFile:myFilePath];

Problem polega na tym, że tabela blokuje się, dopóki obrazy nie zostaną zapełnione (oczywiście dlatego, że jest to żądanie synchroniczne). Na dużym stole powoduje to, że przewijanie jest nie do zniesienia, ale nawet zapełnienie obrazków w pierwszym uruchomieniu może być nudne, jeśli mają znaczący rozmiar.

Dlatego tworzę asynchroniczną klasę żądań, która będzie pobierać dane we własnym wątku wedługDokumentacja Apple. Nie ma problemu. Widzę, że dane są pobierane i zapełniane (za pomocą moich plików dziennika).

Mam problem, gdy mam dane, potrzebuję wywołania zwrotnego do mojej klasy wywołującej (widok tabeli niestandardowej).

Miałem wrażenie, że mogę zrobić coś takiego, ale to nie działa, ponieważ (zakładam), że moja klasa wywołująca naprawdę potrzebuje delegata:

NSImage * myIMage;
myImage = [myConnectionClass getMyImageMethod];

W moim delegacie klasy połączenia widzę, że dostaję dane, po prostu nie widzę, jak przekazać je z powrotem do klasy wywołującej. MójconnectionDidFinishLoading Metoda jest prosto z dokumentów Apple:

- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
    // do something with the data
    // receivedData is declared as a method instance elsewhere
    NSLog(@"Succeeded! Received %d bytes of data",[receivedData length]);

    // release the connection, and the data object
    [connection release];
    [receivedData release];
}

Mam nadzieję, że jest to prosty problem do rozwiązania, ale obawiam się, że jestem na granicy mojej wiedzy na ten temat i pomimo kilku poważnych wyszukiwań w Google i wypróbowania wielu różnych zalecanych podejść, staram się znaleźć rozwiązanie.

W końcu będę miał zaawansowany mechanizm buforowania dla mojej aplikacji, w którym widok tabeli sprawdza lokalną maszynę pod kątem obrazów przed wyjściem i uzyskaniem ich z serwera i może ma wskaźnik postępu, aż obrazy zostaną pobrane. W tej chwili nawet populacja lokalnego obrazu może być powolna, jeśli obraz jest wystarczająco duży przy użyciu procesu synchronicznego.

Każda pomoc byłaby bardzo mile widziana.

Aktualizacja rozwiązania

W przypadku, gdy ktoś inny potrzebuje podobnego rozwiązania, dzięki pomocy Bena, oto, co wymyśliłem (ogólnie zmodyfikowane w celu opublikowania). Pamiętaj, że zaimplementowałem również niestandardowe buforowanie obrazów i sprawiłem, że moja klasa ładowania obrazów jest wystarczająco ogólna, aby mogła być używana przez różne miejsca w mojej aplikacji do wywoływania obrazów.

W mojej metodzie wywoływania, która w moim przypadku była niestandardową komórką w tabeli ...

ImageLoaderClass * myLoader = [[[ImageLoaderClass alloc] init] autorelease];

    [myLoader fetchImageWithURL:@"/my/thumbnail/path/with/filename.png"
                     forMethod:@"myUniqueRef"
                        withId:1234
                   saveToCache:YES
                     cachePath:@"/path/to/my/custom/cache"];

Spowoduje to utworzenie instancji klasy myLoader i przekazanie jej 4 parametrów. Adres URL obrazu, który chcę uzyskać, unikalne odniesienie, którego używam do określenia, która klasa wywołała połączenie podczas konfigurowania obserwatorów powiadomień, identyfikator obrazu, czy chcę zapisać obraz w pamięci podręcznej czy nie i ścieżkę do pamięci podręcznej.

Moja ImageLoaderClass definiuje metodę zwaną powyżej, w której ustawiam to, co jest przekazywane z komórki wywołującej:

-(void)fetchImageWithURL:(NSString *)imageURL 
           forMethod:(NSString *)methodPassed 
              withId:(int)imageIdPassed 
         saveToCache:(BOOL)shouldISaveThis
           cachePath:(NSString *)cachePathToUse
{

    NSURLRequest *theRequest=[NSURLRequest requestWithURL:[NSURL URLWithString:imageURL]
                                          cachePolicy:NSURLRequestUseProtocolCachePolicy
                                      timeoutInterval:60.0];

    // Create the connection with the request and start loading the data

    NSURLConnection *theConnection=[[NSURLConnection alloc] initWithRequest:theRequest delegate:self];

    if (theConnection) {

        // Create the NSMutableData that will hold
        // the received data 
        // receivedData is declared as a method instance elsewhere

        receivedData = [[NSMutableData data] retain];

        // Now set the variables from the calling class

        [self setCallingMethod:methodPassed];
        [self setImageId:imageIdPassed];
        [self setSaveImage:shouldISaveThis];
        [self setImageCachePath:cachePathToUse];

    } else {
        // Do something to tell the user the image could not be downloaded
    }
}

wconnectionDidFinishLoading metoda W razie potrzeby zapisałem plik w pamięci podręcznej i wykonałem powiadomienie do dowolnego obserwatora słuchającego:

- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
    NSLog(@"Succeeded! Received %d bytes of data",[receivedData length]);

    // Create an image representation to use if not saving to cache
    // And create a dictionary to send with the notification    

    NSImage * mImage = [[NSImage alloc ] initWithData:receivedData];
    NSMutableDictionary * mDict = [[NSMutableDictionary alloc] init];

    // Add the ID into the dictionary so we can reference it if needed

    [mDict setObject:[NSNumber numberWithInteger:imageId] forKey:@"imageId"];

    if (saveImage)
    {
        // We just need to add the image to the dictionary and return it
        // because we aren't saving it to the custom cache

        // Put the mutable data into NSData so we can write it out

        NSData * dataToSave = [[NSData alloc] initWithData:receivedData];

        if (![dataToSave writeToFile:imageCachePath atomically:NO])
    NSLog(@"An error occured writing out the file");
    }
    else
    {
        // Save the image to the custom cache

        [mDict setObject:mImage forKey:@"image"];
    }

    // Now send the notification with the dictionary

    NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];

    [nc postNotificationName:callingMethod object:self userInfo:mDict];

    // And do some memory management cleanup

    [mImage release];
    [mDict release];
    [connection release];
    [receivedData release];
}

Na koniec w kontrolerze tabeli ustaw obserwatora, który będzie nasłuchiwał powiadomienia i wysyłał go do metody obsługi ponownego wyświetlania komórki niestandardowej:

-(id)init
{
    [super init];

    NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];

    [nc addObserver:self selector:@selector(updateCellData:) name:@"myUniqueRef" object:nil];

    return self;
}

Problem rozwiązany!

questionAnswers(3)

yourAnswerToTheQuestion