Мостовой литой: __bridge_transfer vs __bridge с синтезированным сеттером
Я использую ARC и путаюсь при использовании __bridge_transfer
. У меня есть свойство userName
следующим образом:
@property (nonatomic, retain) NSString *userName;
...
@synthesize userName = _userName;
...
CASE 1:
NSString *name = (__bridge_transfer NSString *)ABRecordCopyCompositeName(person);
self.userName = name;
CASE 2:
self.userName = (__bridge_transfer NSString *)ABRecordCopyCompositeName(person);
где person
имеет тип ABRecordRef
.
В CASE 1 ARC выпустит локальное имя переменной (согласно моему пониманию, исправьте меня, если я ошибаюсь), однако что произойдет в CASE 2? Должен ли я использовать __bridge
в CASE 2 ИЛИ CASE 2 не должен использоваться вообще? в CASE 2 с __bridge_transfer
или __bridge
, как сбалансировать счетчик ссылок?
в CASE 2, с __bridge_transfer
, ARC освободит объект (объект, который передается как аргумент setter (void)setUserName:(NSString *)userName
)?
Ответы
Ответ 1
Когда вы вызываете ABRecordCopyCompositeName()
, кто-то должен выпустить возвращенный объект в какой-то момент. Использование __bridge_transfer
гарантирует, что ARC освободит объект для вас. Без __bridge_transfer
вы должны освободить возвращенный объект вручную. Это только два варианта.
Следовательно, вы должны использовать __bridge_transfer
в обоих случаях.
Хорошее упражнение состоит в том, чтобы вызвать утечку, используя __bridge
вместо __bridge_transfer
, затем используйте Xcode и Instruments, чтобы попытаться найти утечку. Вызывает ли компилятор утечку? Проводит ли статический анализ (Project → Analyze) утечку? Убирают ли приборы утечку? Если это так, вы узнаете, как проверить, разрешает ли проблема __bridge_transfer
.
Ответ 2
Случай 1 и случай 2 эквивалентны. Подумайте об этом так:
Случай 1:
-(void)func {
NSString *name = someObject; // Retain, so +1 on the reference count
self.userName = name; // Retain, so +1 on the reference count
// End of function, name is going out of scope,
// so release name, so -1 on the reference count.
// Total change to the reference count: +1 for self.userName.
}
Случай 2:
-(void)func {
self.userName = someObject; // Retain, so +1 on the reference count
// End of function.
// Total change to the reference count: +1 for self.userName.
}
Таким образом, они работают одинаково. Обратите внимание, что компилятору разрешено отменять пару сохранения и выпуска, если это безопасно. В простом случае, как это, это, безусловно, их преодоление. Думая об этом со всеми изменениями +1 и -1 в счетчике ссылок, просто сделайте это более понятным.
Чтобы ответить на бит о __bridge versus __bridge_transfer: вы вызывали ABRecordCopyCompositeName
, который возвращает ссылку на неуправляемый объект (a CFStringRef
). Copy
в имени функции сообщает вам, что данный объект теперь принадлежит вам, и вам нужно его в конечном итоге отпустить.
Вы можете сделать это, нажав CFRelease
, или вы можете попросить ARC сделать это за вас. __bridge
сообщает ARC, что ему не разрешено брать собственность (другими словами, вы хотите выпустить объект вручную или он не принадлежит вам). __bridge_transfer
сообщает ARC, что он должен взять на себя ответственность и освободить объект в конце полного выражения (другими словами, вы просите ARC сделать релиз для вас).
С __bridge_transfer
:
self.userName = (__bridge_transfer NSString *)ABRecordCopyCompositeName(person); // +1 inside ABRecordCopyCompositeName, +1 for self.userName, -1 at the end, because of the __bridge_transfer.
// self.userName now is the only strong reference. Good.
С __bridge
:
CFStringRef userName = ABRecordCopyCompositeName(person); // +1 inside ABRecordCopyCompositeName.
self.userName = (__bridge NSString *)userName; // +1 for self.userName, ARC does nothing because of the __bridge.
CFRelease(userName); // -1.
// self.userName now is the only strong reference. Good.
С __bridge
и утечкой памяти:
self.userName = (__bridge NSString *)ABRecordCopyCompositeName(person); // +1 inside ABRecordCopyCompositeName, +1 for self.userName, ARC does nothing because of the __bridge.
// self.userName now is one strong reference, but reference count is 2.
// Memory leak.
Ответ 3
Именно потому, что это сбивает с толку, я рекомендую вам использовать CFBridgingRelease()
и CFBridgingRetain()
, а не прикладывать __bridge_transfer
и __bridge_retained
соответственно. Тогда единственный "необычный" бросок, который вам нужно запомнить, - это __bridge
, который ничего не делает с правами собственности.
Мне легче запомнить, потому что с чем-то вроде ABRecordCopyCompositeName()
, который оставляет вас на CFRelease()
возвращенном объекте, вы можете использовать CFBridgingRelease()
для выполнения этой ответственности, и аналогия очевидна.
Аналогично, вы использовали бы только CFBridgingRetain()
в контексте, где вы бы использовали CFRetain()
, если указатель объекта уже был типом Core Foundation.
Итак, ваш код может быть:
NSString *name = CFBridgingRelease(ABRecordCopyCompositeName(person));
self.userName = name;
Или:
self.userName = CFBridgingRelease(ABRecordCopyCompositeName(person));
В обоих случаях CFBridgingRelease()
балансирует Copy
в имени функции, что подразумевает, что вы несете ответственность за выпуск объекта. После этого все это несет ответственность. ARC управляет переменной name
. Этому управляет средство настройки для свойства userName
. (В данном случае это тоже ARC, но это не имеет значения.)