bridged cast: __bridge_transfer vs __bridge with s

2019-02-04 22:00发布

问题:

I am using ARC, and have confusion while using __bridge_transfer. I have a property userName as following:

@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);

where person is of type ABRecordRef.

In CASE 1, ARC would release local variable name (as per my understanding, correct me if I am wrong), however what would happen in CASE 2 ? Should I use __bridge in CASE 2 OR CASE 2 should not be used at all ? in CASE 2 with __bridge_transfer or __bridge, how to balance the reference count ?

in CASE 2, with __bridge_transfer, will ARC release the object (the object, which is being passed as an argument to the setter (void)setUserName:(NSString *)userName)?

回答1:

When you call ABRecordCopyCompositeName(), someone must release the returned object at some point. Using __bridge_transfer ensures that ARC will release the object for you. Without __bridge_transfer, you must release the returned object manually. Those are the only two options.

Therefore, you must use __bridge_transfer in both cases.

A nice exercise is to induce a leak by using __bridge instead of __bridge_transfer, then use Xcode and Instruments to try and find the leak. Does the compiler pick up the leak? Does static analysis (Project -> Analyze) pick up the leak? Does Instruments pick up the leak? If so, you'll then know how to check whether __bridge_transfer solves the problem.



回答2:

Case 1 and case 2 are equivalent. Think of it like this:

Case 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.
}

Case 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.
}

So they work out the same. Note that the compiler is allowed to cancel out a retain and release pair, if it is safe to do so. In a simple case like this it would certainly elide them. Thinking about it with all the +1 and -1 changes to the reference count is just to make it clearer.

To answer the bit about __bridge versus __bridge_transfer: you have called ABRecordCopyCompositeName, which returns a reference to an unmanaged object (a CFStringRef). The Copy in the function name tells you that this object is now owned by you, and you need to release it eventually.

You can either do this by calling CFRelease, or you can ask ARC to do it for you. __bridge tells ARC that it is not allowed to take ownership (in other words, you want to release the object manually, or it isn't owned by you). __bridge_transfer tells ARC that it should take ownership and release the object at the end of the full expression (in other words, you are asking ARC to do the release for you).

With __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.

With __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.

With __bridge and a memory leak:

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:

Precisely because this is confusing, I recommend that you use CFBridgingRelease() and CFBridgingRetain() rather than casts with __bridge_transfer and __bridge_retained, respectively. Then, the only "unusual" cast you need to remember is __bridge, which does nothing with ownership.

I find it easier to remember because, with something like ABRecordCopyCompositeName() which leaves you with the responsibility to CFRelease() the returned object, you can use CFBridgingRelease() to discharge that responsibility and the analogy is obvious.

Similarly, you'd only use CFBridgingRetain() in a context where you would use CFRetain() if the object pointer were already a Core Foundation type.

So, your code could be:

NSString *name = CFBridgingRelease(ABRecordCopyCompositeName(person));
self.userName = name;

Or:

self.userName = CFBridgingRelease(ABRecordCopyCompositeName(person));

In both cases, the CFBridgingRelease() balances the Copy in the function name that implies that you have a responsibility to release the object. After that, it's all somebody else's responsibility. ARC manages the name variable. The implementer of the setter for the userName property manages that. (It happens to be ARC in this case, too, but that's irrelevant.)