Awesome-o
Awesome-o

Reputation: 2070

Is a __bridge_transfer valid on a NULL object

Let's say a method returns a CFErrorRef via a pointer. This returned error may be NULL. So would it be safe to perform a __bridge_transfer still or should I check for NULL.

E.g.

CFErrorRef cfError;
ABAddressBookRef addressBookRef = ABAddressBookCreateWithOptions(NULL, &cfError);

NSError *error = (__bridge_transfer NSError *)cfError;

I don't see any mention of this in the documentation and CFRelease documentation specifically states This value must not be NULL. https://developer.apple.com/library/mac/documentation/CoreFoundation/Reference/CFTypeRef/Reference/reference.html#//apple_ref/c/func/CFRelease

Upvotes: 4

Views: 1004

Answers (4)

Steven Fisher
Steven Fisher

Reputation: 44886

The direct answer to the question is yes, you can use __bridge_transfer on NULL. But this isn't the right question.

Read the documentation on ABAddressBookCreateWithOptions. In particular, check out the documentation for error:

On error, contains error information. See “Address Book Errors.”

This is important.

  1. error's value in the case of success is not documented.
  2. error being nil/NULL/0 (ever) is not documented.

This isn't academic. Some APIs have historically set error to invalid values. Imagine the call set the CFError to -1. That's "valid" since the non-NULL reply means you're not supposed to interpret the error, but bridge casting -1 to a NSError will probably crash.

That means you must not touch cfError unless an error is indicated by ABAddressBookCreateWithOptions returning NULL.

CFErrorRef cfError;
NSError *error;
ABAddressBookRef addressBookRef = ABAddressBookCreateWithOptions(NULL, &cfError); 
if (addressBookRef == NULL) {
    error = (__bridge_transfer NSError *)cfError;
}

You didn't ask this, but one additional wrinkle here is that bridges aren't even required if the compiler recognizes that something is 0-equivalent. For instance, this code will compile silently (assuming _thing1 and _thing2 are instance variables):

- (id)bar {
    if (_thing1) return NO;
    if (_thing2) return 0;
    return NULL;
}

This is sloppy code, and I you should not do this intentionally, but knowing it builds cleanly… it's a good thing to look for. I ran into a bug caused by something like this:

- (NSNumber *)someCalculationWithError:(NSError *)error {
   return 0; // meant to return @(0)
}

Upvotes: 2

uchuugaka
uchuugaka

Reputation: 12782

The error will be non NULL if the return value of the function is NULL. The pattern for this kind of CF function is to wrap the error checking in an if statement. if (addressBookRef == NULL) { /* your error handling here */}

You should not try to bridge anything unless it is non NULL. Object ownership or more accurately retain count and responsibility for decrementing it, are not meaningful with NULL or nil. It would be an anti pattern. At best it's a null operation. Sending messages to nil with Objective-C is fine, including retain and release. It is not fine to pass a NULL value to CFRelease() or CGRetain()

Upvotes: 2

Darren
Darren

Reputation: 25619

You do not need to check for NULL.

ARC is a strictly compile-time mechanism. When you use __bridge_transfer you are merely transferring memory management responsibility of a variable to the compiler. Whether cfError happens to be NULL or not at runtime is completely irrelevant to the compiler.

In your case, ARC will insert a release for error, but if error happens to be nil it's a simple no-op.

Upvotes: 3

Duncan C
Duncan C

Reputation: 131481

Unlike NSObjects, sending messages to NULL CF objects is not ok. I don't know about bridging casts specifically, but I would guess that no, casting a CF object to an NSObject using __bridge_transfer is NOT ok.

Why not try it and see? Cast it to a variable in the local scope of an instance method. That way, as soon as the method goes out of scope the system should try to release the object.

Upvotes: 0

Related Questions