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

Спасибо!

14
задан Peter Hosey 26 January 2010 в 21:59
поделиться

3 ответа

Два в одном запросе LINQ (и один переход):

arr.Aggregate(
    new { MinDate = DateTime.MaxValue,
          MaxDate = DateTime.MinValue },
    (accDates, record) => 
        new { MinDate = record.Date < accDates.MinDate 
                        ?  record.Date 
                        : accDates.MinDate,
              MaxDate = accDates.MaxDate < record.Date 
                        ?  record.Date 
                        : accDates.MaxDate });
-121--2294756-

Используемый синтаксис называется инициализация с двойной скобкой - это фактически « блок инициализации экземпляра , который является частью анонимного внутреннего класса » (конечно, не взлом). Таким образом, при использовании этой нотации вы фактически определяете новый класс (!).

В вашем случае «проблема» заключается в том, что HashMap реализует Serializable . Этот интерфейс не имеет методов и служит только для определения семантики сериализуемости . Другими словами, это интерфейс маркера, и вам конкретно не нужно ничего реализовывать. Но во время десериализации Java использует номер версии, называемый serureVersionUID , для проверки совместимости сериализованной версии с целевым объектом. Если вы не предоставите этот serureVersionUID , он будет вычислен. И, как описано в javadoc Serializable , вычисленное значение является чрезвычайно чувствительным и поэтому рекомендуется явно объявить его, чтобы избежать каких-либо проблем десериализации. И вот на что «жалуется» Eclipse (заметьте, что это всего лишь предупреждение).

Поэтому, чтобы избежать этого предупреждения, вы можете добавить serureVersionUID к своему одноименному внутреннему классу:

someMethodThatTakesAHashMap(new HashMap<String, String>() {
    private static final long serialVersionUID = -1113582265865921787L;

    {
        put("a", "value-a");
        put("c", "value-c");
    }
});

Но вы теряете лаконичность синтаксиса (и он может вам даже не понадобиться).

Таким образом, другой вариант состоит в игнорировании предупреждения, добавляя @ SupperWarnings («последовательные») к методу, в котором вызывается someMethodthreadHAHashMap (Map) . Это кажется более уместным в вашем случае.

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

Поэтому, хотя я люблю использовать его в тестах (для краткости), я склонен избегать использования его в «обычном» коде.

-121--2551499-

Здесь используется правило « -отключение *, если оно вам не нужно».

Поскольку вы больше не нуждаетесь в поставщике и imageRef после этого, вы должны -разжать все из них, т.е.

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

пиксель не управляется с помощью ref-count, поэтому вы должны сообщить API CG освободить их для вас, когда это необходимо. Сделайте это:

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

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

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

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

21
ответ дан 1 December 2019 в 09:32
поделиться
 unsigned char * pixels = malloc(...);

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

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

Графика ядра следует правилам основы ядра. Вы являетесь владельцем провайдера данных, потому что вы создали его.

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

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

Объект CGImage принадлежит вам, потому что вы его создали.

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

Объект UIImage принадлежит вам, потому что вы его выделили.

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

return [image autorelease];

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

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

8
ответ дан 1 December 2019 в 09:32
поделиться

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

Как насчет разрушения этого на два метода. Измените это MakeanImage в Makeanimageref и подтягивая создание Uiimage в другой селектор OBJ-C.

-2
ответ дан 1 December 2019 в 09:32
поделиться
Другие вопросы по тегам:

Похожие вопросы: