Ошибка: «архиватор завершен; больше ничего не может закодировать»

#objective-c #ios #cocoa-touch #uiimage #grand-central-dispatch

#objective-c #iOS #cocoa-touch #uiimage #grand-central-dispatch

Вопрос:

Я намереваюсь загрузить и закодировать изображение, как показано ниже.

Однако я получаю сообщение об ошибке -[NSKeyedArchiver encodeObject:forKey:]: архиватор завершен; больше ничего не удается закодировать «. Кто-нибудь может объяснить, почему произошла эта ошибка и как я могу ее устранить?

 - (void)encodeWithCoder:(NSCoder *)encoder
{
    dispatch_queue_t downloadQueue = dispatch_queue_create("image downloader", NULL);
        dispatch_async(downloadQueue, ^{
            NSURL *url = [NSURL URLWithString:self.avatar_url];
            NSData *data = [NSData dataWithContentsOfURL:url];
            dispatch_async(dispatch_get_main_queue(), ^{
                self.avatar = [[[UIImage alloc] initWithData:data] autorelease];
                [encoder encodeObject:UIImagePNGRepresentation(self.avatar) forKey:@"avatar"];
            });

        });
}
  

Стек ошибок

 2011-11-03 00:08:32.645 onethingaday[6897:207] *** Terminating app due to uncaught exception 'NSInvalidArchiveOperationException', reason: '*** -[NSKeyedArchiver encodeObject:forKey:]: archiver has finished; cannot encode anything more'
*** First throw call stack:
(0x29de052 0x26ddd0a 0x2986a78 0x29869e9 0x16e1d40 0x9fdd 0x1d33445 0x1d354f0 0x2915833 0x2914db4 0x2914ccb 0x2d57879 0x2d5793e 0xd89a9b 0x290d 0x2885 0x1)
  

Комментарии:

1. поскольку загрузка изображения приводит к зависанию моего приложения, поэтому я хочу сделать его асинхронным с помощью GCD. Есть ли какие-либо проблемы при использовании GCD здесь?

Ответ №1:

Вы не должны использовать асинхронный метод в -encodeWithCoder: . Причина проста. Функция обычно вызывается следующим образом (на более низком уровне):

 NSKeyedArchiver* archiver = [NSKeyedArchiver initForWritingWithMutableData:...];
...
[archiver encodeObject:yourObject forKey:@"key"];  // A
// ^ this calls [yourObject encodeWithCoder:archiver]
...
[archiver finishEncoding];   // B
  

Ваш -encodeWithCoder: асинхронный с GCD. Поэтому строка A вернется до того, как что-либо произойдет, а затем будет выполнена строка B (все равно ваша запланированная функция не будет вызвана). Это завершает архиватор и предотвращает дальнейшее кодирование на нем.

Позже разрешается запустить асинхронный метод, и вы загружаете изображение и конвертируете его в PNG и т. Д. И, Наконец, сообщаете encoder -encodeObject:forKey кому. Но уже слишком поздно — архиватор уже давно завершен! Поэтому генерируется исключение.

Чтобы избежать этого, вы должны убедиться, что не вызываете -encodeObject:forKey асинхронно. Коды GCD должны быть помещены за пределы -encodeWithCoder: метода, т. Е. Изображение должно быть полностью доступно перед выполнением архивирования.