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 asCFStringRef
). 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 fromNSArray
s andNSDictionarie
s.
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.