CF_RETURNS_RETAINED или CF_RETURNS_NOT_RETAINED: что использовать, когда?

#objective-c #automatic-ref-counting #swift #core-foundation

#objective-c #автоматический подсчет ссылок #swift #ядро-основа

Вопрос:

Я не уверен, использовать ли CF_RETURNS_RETAINED или CF_RETURNS_NOT_RETAINED для моей пользовательской функции, возвращающей a CFDataProviderRef .

Согласно документации в том месте, где определены макросы, оба должны использоваться только в исключительных случаях, и правильное исправление должно заключаться в исправлении моего соглашения об именах. Однако документация swift / objective-c предлагает использовать их для аннотирования любой функции, возвращающей указатель CoreFoundation, без реального объяснения, когда использовать какой — если я не аннотирую их, мне нужно вручную указывать поведение каждый раз в коде swift.

Дополнительная документация, которую я смог найти, объясняет, как использовать значение ARC, равное 1, а другое — 0, однако, боюсь, это не очень помогает мне в понимании.

Мои вопросы:

  1. Должен ли я использовать их вообще или улучшить свое именование, как Base.h предлагается?
  2. Какое точное соглашение об именовании там используется?
  3. Правильно ли я предполагаю, что мне следует использовать один, если я хочу, чтобы память, используемая возвращаемым объектом, освобождалась, как только она выходит за пределы области видимости в вызывающем объекте, а другой, если я не хочу, чтобы это произошло (потому что я использую указатель на него где-то в другом месте или очищаю себя)?
  4. Все, что делает моя функция, это возвращает a CFDataProviderRef , который я получил от вызова CGDataProviderCreateSequential . Я думаю, это означает, что я хочу, чтобы поведение было таким CGDataProviderCreateSequential (правильно?). Как я могу узнать, использует ли эта функция CF_RETURNS_RETAINED или CF_RETURNS_NOT_RETAINED (его нет в CGDataProvider.h файле)?

Ответ №1:

Это все о собственности. Соглашение об именовании соответствует соглашению Core Foundation, которое описано здесь . Правило создания гласит, что функции с «Create» и «Copy» в их имени оставляют вызывающему право собственности на возвращаемый объект (хотя и не обязательно единоличное право собственности). Это означает, что вызывающий объект несет ответственность за то, чтобы в конечном итоге освободить возвращаемый объект с помощью CFRelease() . Правило Get гласит, что функции, отличные от функций создания или копирования, не дают вызывающему объекту права собственности на объект, поэтому вызывающий объект не должен вызывать CFRelease() его (за исключением CFRetain() , конечно, балансировки любых явных вызовов).

Если функция семантически является функцией создания или копирования, которая возвращает право собственности вызывающей стороне, но ее имя не указывает на это, вы должны использовать CF_RETURNS_RETAINED для указания этого. Аналогично, если имя функции содержит «Создать» или «Копировать», но у него нет семантики, соответствующей правилу создания, вы должны использовать CF_RETURNS_NOT_RETAINED для указания этого. (Старайтесь избегать этого.)

Поскольку ваш код вызывает CGDataProviderCreateSequential() и поскольку в его имени есть «Create», ваш код отвечает за освобождение возвращаемого CGDataProvider объекта. Если вы освободите его прямо в своей функции, ваш вызывающий не сможет получить к нему доступ. Вы хотите вернуть объект вызывающему. Вы также хотите передать ответственность за освобождение объекта вызывающему, поскольку вы не знаете, когда вызывающий закончит с ним. Итак, вы должны назвать свою функцию с «Create» в ее имени, чтобы указать как вызывающему, так и автоматизированным системам, что вызывающий получает ответственность за освобождение объекта. В качестве альтернативы, вы могли бы аннотировать свою функцию, CF_RETURNS_RETAINED чтобы обозначить это, но лучше следовать соглашению об именовании.

Возможно, что Swift соблюдает соглашение об именовании только для системных заголовков. Я не знаю. В этом случае вам придется аннотировать с CF_RETURNS_RETAINED помощью, даже если в имени вашей функции есть «Create». Нет никакого вреда в аннотировании функции, имя которой уже соответствует соглашению. Это избыточно, но безвредно.

Комментарии:

1. Спасибо за подробный ответ, именно то, что я искал. Есть ли шанс, что вы можете прокомментировать само именование? CF_RETURNS_RETAINED, похоже, предполагает, что вызываемая функция сохраняет указатель на объект, однако, как вы это объясняете (и, возможно, источник моего первоначального замешательства), все с точностью до наоборот (CF_RETURNS_NOT_RETAINED сохраняет указатель в вызываемой функции). Так что, вероятно, я должен думать об этом по-другому… Я могу подтвердить, что (текущая версия) swift не использует соглашение об именовании для моей пользовательской функции CGDataProviderCreateFileHandle .

2. Я согласен, что имена не очень хороши. Концептуально, вы используете CFRelease() для балансировки сохранения. Итак, функция, которая возвращает что-то, что вам нужно освободить, возвращает что-то, что было сохранено от вашего имени в этом смысле. Кстати, не называйте свою собственную функцию CGDataProviderCreateFileHandle() . Это создает впечатление, что это рамочная функция. Используйте какой-нибудь префикс, отличный от «CG». (Также создается впечатление, что вы запрашиваете создание дескриптора файла. Возможно, вы захотите вставить туда «With».)

3. Небольшая поправка: «Правило Get» применяется (если я правильно понимаю) ко всем функциям, в названии которых нет «Create» или «Copy». Не требуется, чтобы в их имени было «Get». Нет случая «не соответствует соглашению об именах».

4. @MartinR хотя изначально это было правильно, они, похоже, сделали это более понятным с появлением ARC и / или swift. Теперь у Swift есть три варианта обработки возвращаемых результатов C: owned, unowned и unknown. Если бы все еще было верно, что get не требуется, то не было бы случая, когда swift не смог бы понять это и должен был вернуться к использованию любой оболочки, которую он использует для неизвестных случаев сохранения. Извините, я не более конкретен, не в том месте, где я могу легко его найти.

5. @David: Мой комментарий был связан с «Руководством по программированию управления памятью для Core Foundation», где у вас есть только два случая: либо «Создать правило», либо «Получить правило» (и я не думаю, что это изменилось с ARC). Возможно, я ошибаюсь, но я не думаю, что есть какая-либо документация, в которой упоминаются функции «с «Get» в их названии» .