#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, однако, боюсь, это не очень помогает мне в понимании.
Мои вопросы:
- Должен ли я использовать их вообще или улучшить свое именование, как
Base.h
предлагается? - Какое точное соглашение об именовании там используется?
- Правильно ли я предполагаю, что мне следует использовать один, если я хочу, чтобы память, используемая возвращаемым объектом, освобождалась, как только она выходит за пределы области видимости в вызывающем объекте, а другой, если я не хочу, чтобы это произошло (потому что я использую указатель на него где-то в другом месте или очищаю себя)?
- Все, что делает моя функция, это возвращает 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» в их названии» .