Мостовой литой: __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, но это не имеет значения.)