Какой правильный шаблон управления памятью для буфера-> CGImageRef-> UIImage?
У меня есть функция, которая принимает некоторые растровые данные и возвращает 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];
}
Может кто-нибудь объяснить, кто владеет какой памятью здесь? Я хочу правильно очистить, но я не уверен, как сделать это безопасно. Документы нечеткие. Если я free
пикселей в конце этой функции после создания UIImage, а затем использовать UIImage, я сбой. Если я отпущу провайдера или imageRef после создания UIImage, я не вижу сбоя, но они, очевидно, пропускают пиксели на всем протяжении, так что я очень люблю выпускать эти промежуточные состояния.
(Я знаю, что в CF документах мне нужно будет вызывать выпуск на обоих последних, потому что они происходят из функций Create, но могу ли я сделать это до использования UIImage?) Предположительно, я могу использовать обратный вызов dealloc провайдера для очистки буфер пикселей, но что еще?
Спасибо!
Ответы
Ответ 1
Правило большого пальца здесь "-release
*, если оно вам не нужно".
Поскольку вам больше не нужны provider
и imageRef
, вы должны -release
все из них, т.е.
UIImage * image = [[UIImage alloc] initWithCGImage:imageRef];
CGDataProviderRelease(provider);
CGImageRelease(imageRef);
return [image autorelease];
pixel
не управляется подсчетом ссылок, поэтому вам нужно сообщить CG API, чтобы освободить их для вас, когда это необходимо. Сделайте это:
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
уже предположительно равен нулю с самого начала.)
Ответ 2
unsigned char * pixels = malloc(...);
У вас есть буфер pixels
, потому что вы его разблокировали.
CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, pixels, pixelBufferSize, NULL);
Core Graphics следует правилам Core Foundation. У вас есть поставщик данных, потому что вы Создали его.
Вы не предоставили обратный вызов для релиза, поэтому вы по-прежнему владеете буфером pixels
. Если вы предоставили обратный вызов для выпуска, объект CGDataProvider будет владеть этим буфером. (Как правило, хорошая идея.)
CGImageRef imageRef = CGImageCreate(..., provider, ...);
У вас есть объект CGImage, потому что вы его создали.
UIImage * image = [[UIImage alloc] initWithCGImage:imageRef];
У вас есть объект UIImage, потому что вы его разблокировали.
У вас также есть собственный объект CGImage. Если объект UIImage хочет владеть объектом CGImage, он либо сохранит его, либо сделает свою собственную копию.
return [image autorelease];
Вы отказываетесь от своей собственности на изображение.
Таким образом, ваш код утечки пикселей (вы не передали право собственности поставщику данных, и вы сами не выпустили их), поставщик данных (вы его не выпустили) и CGImage (вы не отпустите его). Фиксированная версия передаст права собственности на пиксели поставщику данных и освободит как поставщика данных, так и CGImage к тому времени, когда UIImage будет готов. Или просто используйте imageWithData:
, как предположил KennyTM.
Ответ 3
unsigned char * pixels = malloc(...);
У меня также была проблема с malloc/free после использования CGImageCreate
Наконец-то я нашел хорошее и простое решение.
Я просто заменю строку:
CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, pixels, pixelBufferSize, NULL);
с:
NSData *data = [NSData dataWithBytes:pixels length:pixelBufferSize];
CGDataProviderRef provider = CGDataProviderCreateWithCFData((CFDataRef)data);
Сразу после этого я могу освободить mallocked память:
free (pixels);
Ответ 4
Да, этот код меня тошнит. В качестве старого правила для жизни я стараюсь не смешивать и сопоставлять C и С++, а C/ Objective-C в той же функции/методе/селекторе.
Как разбить это на два метода. Измените это makeAnImage на makeAnImageRef и потяните создание UIImage в другой селектор Obj-C.