Какой правильный шаблон управления памятью для buffer-> CGImageRef-> UIImage? - PullRequest
14 голосов
/ 26 января 2010

У меня есть функция, которая принимает некоторые растровые данные и возвращает из них 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, что мне нужно вызывать release для обоих из последних, потому что они приходят из функций Create, но могу ли я сделать это до использования UIImage?) Предположительно, я могу использовать callloc-вызов dealloc провайдера для очистки буфер пикселей, а что еще?

Спасибо!

Ответы [ 4 ]

21 голосов
/ 26 января 2010

Правило большого пальца здесь "-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:, поэтому вам не нужно возиться с компьютерной графикой и malloc.

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

8 голосов
/ 27 января 2010
unsigned char * pixels = malloc(...);

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

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

Базовая графика соответствует правилам 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.

1 голос
/ 02 февраля 2012
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);

Сразу после этого я могу освободить заблокированную память:

free (pixels);
0 голосов
/ 26 января 2010

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

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

...