2010-01-26 1 views
14

У меня есть функция, которая принимает некоторые растровые данные и возвращает UIImage * из нее. Это выглядит примерно так:Какой правильный шаблон управления памятью для буфера-> CGImageRef-> UIImage?

UIImage * makeAnImage() 
{ 
    unsigned char * pixels = malloc(...); 
    // ... 
    CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, pixels, pixelBufferSize, NULL); 
    CGImageRef imageRef = CGImageCreate(..., provider, ...); 
    UIImage * image = [[UIImage alloc] initWithCGImage:imageRef]; 
    return [image autorelease]; 
} 

Может кто-нибудь объяснить, кто владеет какой памятью здесь? Я хочу правильно очистить, но я не уверен, как сделать это безопасно. Документы нечеткие. Если I free пикселей в конце этой функции после создания UIImage, а затем используйте UIImage, я сбой. Если я отпущу провайдера или imageRef после создания UIImage, я не вижу сбоя, но они, очевидно, пропускают пиксели на всем протяжении, так что я очень люблю выпускать эти промежуточные состояния.

(Я знаю, что для каждого документа, который должен быть вызван релизом, оба из них, поскольку они происходят из функций Create, но могу ли я сделать это до использования UIImage?) Предположительно, я могу использовать обратный вызов dealloc провайдера для очистить буфер пикселей, но что еще?

Спасибо!

ответ

21

Правило большого пальца здесь «-release * оно, если оно вам не нужно».

Потому что вам больше не нужно provider и imageRef после этого, вы должны -release все из них, то есть

UIImage * image = [[UIImage alloc] initWithCGImage:imageRef]; 
CGDataProviderRelease(provider); 
CGImageRelease(imageRef); 
return [image autorelease]; 

pixel не управляет реф-счета, так что вы должны сказать API CG, чтобы освободить их для когда это необходимо. Сделайте это:

void releasePixels(void *info, const void *data, size_t size) { 
    free((void*)data); 
} 
.... 

CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, pixels, pixelBufferSize, releasePixels); 

Кстати, вы можете использовать +imageWithCGImage: вместо [[[* alloc] initWithCGImage:] autorelease]. Еще лучше, есть +imageWithData:, поэтому вам не нужно возиться с CG и malloc.

(*:. За исключением случаев, когда retainCount уже якобы ноль с самого начала)

+0

Спасибо, Кенни. Это красиво сжатое описание; Я думаю, что меня немного бросили непредсказуемость буфера сырой кучи, но, как всегда, верю в правила, и вы будете вознаграждены. Приветствия. –

+1

Чтобы добавить некоторые пояснения, ключевыми словами в основных функциях являются «Создать» и «Создать». Если функция содержит одно из этих слов, вы должны освободить возвращенную память. Большинство основных типов данных совместимы с CFType. Это означает, что вы можете использовать вызовы для сохранения/освобождения/автоматического резервирования Objective-C, если это проще. т.е. [(id) imageRef release]; или CFRelease (imageRef); –

+0

Не забудьте проверить, является ли 'imageRef'' NULL', если вы используете 'CFRelease'. – kennytm

-2

Да, этот код меня тошнит. Как старое правило для жизни, я стараюсь не смешивать и сопоставлять C и C++, а C/Objective-C в той же функции/методе/селекторе.

Как разбить это на два метода. Измените это makeAnImage на makeAnImageRef и потяните создание UIImage в другой селектор Obj-C.

+0

Хотя я понимаю реакцию :) делать это до сих пор не помогите моей проблеме управления памятью. У меня все еще есть проблема, что передача необработанных байтов в начало конвейера оставляет меня с UIImage, который каким-то образом все еще нуждается в этих байтах. Таким образом, этот код более связан, чем может показаться. –

8
unsigned char * pixels = malloc(...); 

Вы владеете pixels буфер, потому что вы mallocked его.

CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, pixels, pixelBufferSize, NULL); 

Core Graphics следует правилам ядра Foundation. У вас есть поставщик данных, потому что вы это Created.

Вы не предоставили обратный вызов, так что у вас все еще есть буфер pixels. Если вы предоставили обратный вызов для выпуска, объект CGDataProvider будет владеть этим буфером. (Как правило, хорошая идея.)

CGImageRef imageRef = CGImageCreate(..., provider, ...); 

У вас есть объект CGImage, потому что вы его создали.

UIImage * image = [[UIImage alloc] initWithCGImage:imageRef]; 

У вас есть объект UIImage, потому что вы его положили.

Вы также являетесь владельцем объекта CGImage. Если объект UIImage хочет владеть объектом CGImage, он либо сохранит его, либо сделает свою собственную копию.

return [image autorelease]; 

Вы сдаете свою собственность на изображение.

Таким образом, ваш код просачивает пиксели (вы не передавали права собственности поставщику данных, и вы сами не выпускали их), провайдер данных (вы его не выпустили) и CGImage (вы не сделали этого, t освободить его). Фиксированная версия передаст права собственности на пиксели поставщику данных и освободит как поставщика данных, так и CGImage к тому времени, когда UIImage будет готов. Или просто используйте imageWithData:, как предложил KennyTM.

+0

Питер: Спасибо, что нашли время, чтобы пройти через это, особенно после того, как вечеринка Q & A закончилась. –

1
unsigned char * pixels = malloc(...); 

Я также имел проблемы с таНосом/свободным после использования CGImageCreate я наконец нашел хорошее и простое решение. я просто заменить строки:

CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, pixels, pixelBufferSize, NULL); 

с:

NSData *data = [NSData dataWithBytes:pixels length:pixelBufferSize]; 
CGDataProviderRef provider = CGDataProviderCreateWithCFData((CFDataRef)data); 

Только после этого я мог бы освободить память mallocked:

free (pixels); 
+1

Для этого необходимо скопировать все байты, которые могут быть неэффективными для некоторых случаев использования. Вместо этого используйте [NSData dataWithBytesNoCopy: length], который возьмет на себя ответственность за байты и освободит их для вас, когда они больше не понадобятся. –