Какой правильный шаблон управления памятью для буфера-> 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.