bridge<T>()

The bridge<T>system function enables the type-safe casting between Cocoa objects and non-OOP CoreFoundation entities. This serves two related purposes:

  • A handful of Foundation objects (such as NSString) can be bridged toll-free to their CoreFoundation counterparts (such as CFStringRef). This makes it easier to write code that uses the Cocoa classes but can still interact with the lower level C-based APIs of CoreFoundation.
  • In addition, a lot of other CoreFoundation entities can be cast to id and be treated as an untyped Cocoa object — for example to be stored in or retrieved from NSArrays and NSDictionaries.

The bridge<T> function acts as a generic function, in that it takes a generic parameter that defines the result type; as regular parameters, it takes the source object or entity to cast and, optionally, a BridgeMode enum value.

Syntax:

method bridge<T>(aObjectOrEntity: Unknown; aBridgeMode: BridgeMode := BridgeMode.Bridge): Unknown;
Unknown void bridge<T>(Unknown objectOrEntity, bridgeMode: BridgeMode = BridgeMode.Bridge);
func bridge<T>(_ objectOrEntity: Unknown, _ bridgeMode: BridgeMode = BridgeMode.Bridge) -> Unknown
Unknown void bridge<T>(Unknown objectOrEntity, bridgeMode: BridgeMode = BridgeMode.Bridge);

It is constrained so that either the generic parameter is a Cocoa object type (i.e. an NSObject or id) and the first passed parameter is a CoreFoundation entity, or the other way around.

BridgeMode.Bridge

BridgeMode.Bridge, the default, does not affect the original object or entity, but merely makes it available as the other type. If the original item was obtained "unowned", the original owner will remain responsible for its life cycle. If the original item was owned, it remains owned and still needs to be released.

Two examples:

var u := UIColor.redColor;
var c := u.CGColor
var o := bridge<id>(c);
UIColor u = UIColor.redColor;
CGColor c = u.CGColor
UIColor o = bridge<id>(c);
let u = UIColor.redColor;
let c = u.CGColor
let o = bridge<id>(c);
UIColor u = UIColor.redColor;
CGColor c = u.CGColor
UIColor o = bridge<id>(c);

Here, o will receive an Objective-C representation of the CGColor entity stored in c. The original UIColor stored in u keeps full ownership of this entity, and if u were to go out of scope or be overwritten, the UIColor would be released and relinquish its hold on the internal CGColor entity still stored in o.

var c := CFStringCreateWithCString(nil, 'hello', CFStringGetSystemEncoding());
var s := bridge<NSString>(c);
CFRelease(c);
CFString c = CFStringCreateWithCString(null, "hello", CFStringGetSystemEncoding());
string s = bridge<NSString>(c);
CFRelease(c);
let c = CFStringCreateWithCString(nil, "hello", CFStringGetSystemEncoding())
let s = bridge<NSString>(c)
CFRelease(c)
CFString c = CFStringCreateWithCString(null, "hello", CFStringGetSystemEncoding());
String s = bridge<NSString>(c);
CFRelease(c);

Here, a CFStringRef entity is created and stored in variable c. It is then bridged to a NSString and stored in s. Because this is a regular bridge cast, the ownership of the original CoreFoundation entity is not affected, c is owned by the current code scope, and does need to be released explicitly. (Releasing it will of course not affect the value in s, which retains individually.)

BridgeMode.Bridge is the equivalent of a regular __bridge cast in Objective-C.

BridgeMode.Transfer

By contrast, BridgeMode.Transfer transfers the ownership of the item to the result of the cast, essentially invalidating the original reference. For reasons that will soon become apparent, this mode only works when casting from CoreFundation entities to Cocoa objects. Using it in the other direction will result in a compiler error.

For example:

var c := CFStringCreateWithCString(nil, 'hello', CFStringGetSystemEncoding());
var s := bridge<NSString>(c, BridgeMode.Transfer);
c := nil;
CFString c = CFStringCreateWithCString(null, "hello", CFStringGetSystemEncoding());
string s = bridge<NSString>(c, BridgeMode.Transfer);
c = null;
var c = CFStringCreateWithCString(nil, "hello", CFStringGetSystemEncoding())
let s = bridge<NSString>(c, BridgeMode.Transfer)
c = nil
CFString c = CFStringCreateWithCString(null, "hello", CFStringGetSystemEncoding());
String s = bridge<NSString>(c, BridgeMode.Transfer);
c = null;

Just like in the sample above, a CFStringRef entity is created and stored in variable c. It is then bridged to a NSString and stored in s. However, since a bridge transfer cast is being performed, ownership is transferred from the CoreFundation entity to the Cocoa object, the NSString object stored in s. The original CoreFoundation no longer needs to (in fact, must not) be freed.

BridgeMode.Transfer is the equivalent of a __bridge_transfer cast in Objective-C.

BridgeMode.Retained

BridgeMode.Retained is the counterpart for transfer when going the opposite direction, from a Cocoa object to a CoreFoundation entity. Since ARC handles all lifecycle management for Cocoa objects, a true transfer is not possible in this direction, as it would leave a Cocoa object that ARC cannot simply and magically know to skip from releasing (after all, references to it could be retained in strong properties and fields all over the place). Instead, BridgeMode.Retained will keep the original Cocoa object intact (just like a plain bridge), but will also bestow full ownership to the CoreFoundation entity. This means that even if the Cocoa object does get released, the CF entity remains valid; it also means that the CF entity now has to be explicitly released.

This mode only works when casting from Cocoa objects to CoreFundation entities. Attempting to use it in the other direction will result in a compiler error.

var s := NSString.stringWithFormat('hello, %@', name);
var c := bridge<CFStringRef>(s, BridgeMode.Retained);
...
CFRelease(c);
NSString s := NSString.stringWithFormat("hello, %@", name);
CGStringRef c = bridge<CFStringRef>(s, BridgeMode.Retained);
...
CFRelease(c);
let s = NSString.stringWithFormat("hello, %@", name)
let c = bridge<CFStringRef>(s, BridgeMode.Retained)
...
CFRelease(c)
NSString s := NSString.stringWithFormat("hello, %@", name);
CGStringRef c = bridge<CFStringRef>(s, BridgeMode.Retained);
...
CFRelease(c);

This time, we're casting the other way. An NSString is created initially, but then bridged into a CoreFoundation entity and stored in c. Ownership is now on c, the CoreFoundation entity, and it does need to be released. However, the original object stored in s remains valid, as well (as it needs to, because ARC will eventually release it).

BridgeMode.Retained is the equivalent of a __bridge_retained cast in Objective-C.

See Also