Я наткнулся на пресловутую стену, пытаясь выяснить, как заполнить NSImage данными, возвращаемыми из асинхронного NSURLConnection в моем настольном приложении (НЕ приложении iPhone !!).
Вот ситуация.
У меня есть таблица, в которой используются пользовательские ячейки. В каждой пользовательской ячейке есть NSImage, который извлекается из веб-сервера. Для заполнения изображения я могу легко выполнить синхронный запрос:
myThumbnail = [[NSImage alloc] initWithContentsOfFile:myFilePath];
Проблема в том, что таблица блокируется до заполнения изображений (очевидно, потому что это синхронный запрос). На большом столе это делает прокрутку невыносимой, но даже простое заполнение изображений при первом запуске может быть утомительным, если они имеют какой-либо значительный размер.
Поэтому я создаю асинхронный класс запросов, который будет извлекать данные в своем собственном потоке согласно документации Apple . Нет проблем там. Я вижу, как данные извлекаются и заполняются (через мои файлы журнала).
Проблема, с которой я столкнулся, заключается в том, что, как только у меня появятся данные, мне потребуется обратный вызов в мой вызывающий класс (настраиваемое представление таблицы).
У меня сложилось впечатление, что я мог бы сделать что-то подобное, но это не сработало, потому что (я предполагаю), что мой вызывающий класс действительно нуждается в делегате:
NSImage * myIMage;
myImage = [myConnectionClass getMyImageMethod];
В моем делегате класса соединения я вижу, что я получаю данные, я просто не вижу, как передать их обратно вызывающему классу. Мой connectionDidFinishLoading
метод прямо из документов 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];
}
Я надеюсь, что это простая проблема, которую нужно решить, но я боюсь, что нахожусь на пределе своих знаний по этому вопросу, и, несмотря на некоторые серьезные поиски в Google и пробуя много разных рекомендуемых подходов, я изо всех сил пытаюсь найти решение.
Со временем у меня будет сложный механизм кэширования для моего приложения, в котором табличное представление проверяет изображения на локальном компьютере перед их отправкой и получает их с сервера и, возможно, имеет индикатор выполнения до тех пор, пока изображения не будут получены. В настоящее время даже локальная популяция изображений может быть медленной, если изображения достаточно велики с использованием синхронного процесса.
Любая помощь будет принята с благодарностью.
Обновление решения
На случай, если кому-то еще понадобится подобное решение, благодаря помощи Бена, вот что я придумал (в общем, модифицированный для публикации, конечно). Имейте в виду, что я также реализовал пользовательское кэширование изображений и сделал свой класс загрузки изображений достаточно универсальным, чтобы его можно было использовать в разных местах моего приложения для вызова изображений.
В моем методе вызова, который в моем случае был пользовательской ячейкой в таблице ...
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"];
Это создает экземпляр класса myLoader и передает ему 4 параметра. URL-адрес изображения, которое я хочу получить, уникальная ссылка, которую я использую, чтобы определить, какой класс выполнял вызов при настройке наблюдателей уведомлений, идентификатор изображения, хочу ли я сохранить изображение в кэш или нет, а также путь в кеш.
Мой ImageLoaderClass определяет метод, вызванный выше, где я устанавливаю то, что передается из вызывающей ячейки:
-(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
}
}
В методе connectionDidFinishLoading
я сохранил файл в кеш, если это было необходимо, и отправил уведомление любому слушающему наблюдателю:
- (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];
}
Наконец, в контроллере таблицы установите наблюдателя для прослушивания уведомления и отправьте его методу для обработки повторного отображения пользовательской ячейки:
-(id)init
{
[super init];
NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
[nc addObserver:self selector:@selector(updateCellData:) name:@"myUniqueRef" object:nil];
return self;
}
Проблема решена!