UIImage в потоке не выпускается/перезаписывается

Похоже, это классический метод сканирования изображений с iPhone. У меня есть поток, который отправляется из основного потока для поиска кодов. По сути, он каждый раз создает новый UIImage, а затем удаляет его.

NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    { 
        while (![thread isCancelled]) {
#ifdef DEBUG
            NSLog(@"Decoding Loop");
#endif
        //  [self performSelectorOnMainThread:@selector(updateImageBuffer) withObject:nil waitUntilDone:YES];           
            CGImageRef cgScreen = UIGetScreenImage();
            UIImage *uiimage = [UIImage imageWithCGImage:cgScreen];

            if (uiimage){
                CGSize size = [uiimage size];
                CGRect cropRect = CGRectMake(0.0, 80.0, size.width, 360); // Crop to centre of the screen - makes it more robust
#ifdef DEBUG
                NSLog(@"picked image size = (%f, %f)", size.width, size.height);
#endif
                [decoder decodeImage:uiimage cropRect:cropRect];
            }
            [uiimage release];
            CGImageRelease(cgScreen);
        }
    }
    [pool release];

проблема в том, что [выпуск пула] вызывает ERROR_BAD_EXC (эта старая классика) и бомбы программы. Мне сказали, что нет необходимости вызывать [релиз uiimage], поскольку я явно не выделил UIImage, но, похоже, это не так. Если я уберу эту строку, использование памяти резко возрастет, и программа завершит работу из-за нехватки памяти. Похоже, я не могу иметь эту работу так, как мне хотелось бы.

Есть ли способ создать UIImage «на месте»? То есть есть буфер, который снова и снова записывается как UIImage? Я подозреваю, что это сработает?

Обновить!

Попытался выполнить связанные с UIKit вызовы в основном потоке следующим образом:

-(void)performDecode:(id)arg{

    // Perform the decoding in a seperate thread. This should, in theory, bounce back with a 
    // decoded or not decoded message. We can quit at the end of this thread.
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    { 
        while (![thread isCancelled]) {

#ifdef DEBUG
            NSLog(@"Decoding Loop");
#endif
            [self performSelectorOnMainThread:@selector(updateImageBuffer) withObject:nil waitUntilDone:YES];           

            if (uiimage){
                CGSize size = [uiimage size];
                CGRect cropRect = CGRectMake(0.0, 80.0, 320, 360); // Crop to centre of the screen - makes it more robust
#ifdef DEBUG
                NSLog(@"picked image size = (%f, %f)", size.width, size.height);
#endif
                [decoder decodeImage:uiimage cropRect:cropRect];
            }
        }
    }
    [pool drain];


#ifdef DEBUG
    NSLog(@"finished decoding.");
#endif


}

-(void) updateImageBuffer {
    CGImageRef cgScreen = UIGetScreenImage();
    uiimage = [UIImage imageWithCGImage:cgScreen];
    //[uiimage release];
    CGImageRelease(cgScreen);
}

Однако никакой радости, поскольку EXC_BAD_ACCESS поднимает свою уродливую голову, когда кто-то хочет захватить «Размер» UIImage


person Oni    schedule 03.11.2009    source источник


Ответы (4)


Как было сказано другими, вы не должны выпускать UIImage, возвращаемый из imageWithCGImage: . Он выпускается автоматически. Когда ваш пул истощается, он пытается отправить сообщение о выпуске вашим уже выпущенным объектам изображения, что приводит к вашему сбою.

Причина, по которой использование вашей памяти продолжает расти, заключается в том, что вы очищаете пул автоматического освобождения только вне цикла. Ваши автоматически выпущенные объекты продолжают накапливаться внутри цикла. (Кстати, вам нужно освободить свой пул автоматического освобождения в конце этого метода, потому что в настоящее время происходит утечка.) Чтобы предотвратить это накопление, вы можете сливать пул через равные промежутки времени в цикле.

Тем не менее, я бы предложил переключиться на выполнение [[UIImage alloc] initWithCGImage:cgScreen], а затем выпустить изображение, когда закончите. Я стараюсь избегать использования автоматически освобождаемых объектов везде, где это возможно, в приложениях iPhone, чтобы лучше контролировать использование памяти и повысить общую производительность.

person Brad Larson    schedule 03.11.2009
comment
Ваше здоровье! Я упомянул об этом в предыдущем комментарии, но спасибо за подсказку с Drain. Суть заключалась в том, чтобы выяснить, что удобные методы связаны с автоматическим выпуском, и поэтому ранний выпуск не работает правильно. - person Oni; 04.11.2009

UIGetScreenImage() является закрытым и недокументированным, поэтому вы не можете его использовать. Сказать, что ничто об этом не говорит о том, что теперь вы владеете CGImageRef cgScreen, так почему вы его выпускаете? У вас также нет возможности узнать, является ли он потокобезопасным, и поэтому следует предположить, что это не так. Затем вы выпускаете IImage *uiimage, который вы не инициализировали, не сохраняли и не копировали, так что, опять же, он вам не принадлежит. Ознакомьтесь с документами.

person Community    schedule 03.11.2009
comment
частное или не-частное для меня не имеет значения в данный момент. Метод выпуска на cgScreen был взят из другого фрагмента кода. Я просмотрел документы и согласен с тем, что uiimage не следует выпускать, ОДНАКО, как я уже сказал, память просто продолжает подниматься, если эта строка не помещена на место. - person Oni; 03.11.2009

[uiimage release] определенно неверен в этом контексте. Также Apple подчеркивает, что все методы UIKit должны выполняться в основном потоке. В том числе UIGetScreenImage() и +[UIImage imageWithCGImage:].

Изменить: Таким образом, вы получаете исключение при вызове -[UIImage size] в неправильном потоке. Это, вероятно, не должно вас удивлять, потому что это не разрешено.

person Ole Begemann    schedule 03.11.2009
comment
Да, я так и думал. Я вырезал это, но внутренняя библиотека, которую я использую, делает такие вещи и с UIImage, так что... не в восторге от этого. - person Oni; 03.11.2009

UIImage *uiimage = [[UIImage alloc] initWithCGImage: cgScreen];

Явное заявление о том, что я лучше знаю, когда освобождать объект, похоже, сработало. Виртуальная память по-прежнему увеличивается, но физическая теперь остается неизменной. Однако спасибо за указание на проблемы с безопасностью потоков UIKit. Это момент, который я пропустил, но, похоже, на данный момент это не влияет на работу.

Кроме того, я должен отметить, что Red Laser и Quickmark используют этот метод сканирования информации о камере;)

person Oni    schedule 03.11.2009