The native API on OS X is spread over several layers. Some of these are written in (and designed for) Objective-C, but others are just C-based APIs. An example of the latter is the POSIX layer, which is the API shared with other Unix and Unix-like operating systems such as Linux.
Roughly speaking, POSIX has a feel akin to the traditional Windows API, if not completely – lower case identifiers rule, and text encoding is UTF-8 rather than UTF-16 or ‘Ansi’. Beyond it stands the ‘Core’ APIs such as Core Foundation and Core Graphics. Unlike POSIX, these are Mac specific, and use a pseudo-object oriented approach within the bounds of a straight C interface. For example, instead of using C-style ‘strings’ (PAnsiChar or PWideChar in Delphi terms), the Core APIs have ‘CFString’ and ‘CFMutableString’ pseudo-object types. These act a bit like the ‘handle’ types you have in the Windows API, in which every window has an HWND, every graphical output destination (‘device context’) an HDC, and so on. Indeed, the analogy goes further, because just like you never see a ‘WND’ or ‘DC’ type in code (instead, you use HWND and HDC), so you never see CFString or CFMutableString: rather, you use CFStringRef and CFMutableStringRef. This can seem odd at first, but only because Delphi classes are reference not value types, meaning the ‘Ref’ thing is effectively implicit in pure Delphi code.
Where the analogy with Windows API handles falls down though is in how Core XXX ‘objects’ are reference counted. The reference counting model used is similar to the IUnknown/IInterface one, though not exactly – in particular, weak references are much more common when using the Apple API, i.e. where the reference count (in Apple-speak, ‘retain count’) hasn’t been incremented. Because of this, automatic reference counting in the way it implemented by the Delphi compiler for IUnknown/IInterface isn’t realistic, and instead, reference counting is all manual. When using Core Foundation objects, this means calling CFRelease and CFRetain as appropriate: every time you get a CF object from a function with ‘Create’ or ‘Copy’ in its name, you will need to call CFRelease to have it freed properly; conversely, receiving an object via a function without ‘Create’ or ‘Copy’ in its name may require calling CFRetain (then CFRelease later) to avoid the object being freed from under your feet.
Like Delphi, Core Foundation’s native string encoding is UTF-16. To convert from a Delphi to a CFString, call CFStringCreateWithCharacters.
uses Macapi.CoreFoundation; function DelphiToCFString(const S: string): CFStringRef; inline; begin Result := CFStringCreateWithCharacters(nil, PChar(S), Length(S)); end;
If you look at the FMX source, you may get the idea you need to go through a conversion to UTF-8 – you don’t however. Anyhow, to go the other way, call CFStringGetCharacters:
function CFToDelphiString(const CFStr: CFStringRef): string; var Range: CFRange; begin if CFStr = nil then Exit(''); Range.location := 0; Range.length := CFStringGetLength(CFStr); SetLength(Result, Range.length); CFStringGetCharacters(CFStr, Range, PChar(Result)); end;
Since CFString is ‘toll-free bridged’ with the Cocoa/Objective-C equivalent, you can use the above routines to convert to and from NSString too (or more exactly, the NSString wrapper interface type):
uses Macapi.ObjectiveC, Macapi.Foundation; function DelphiToNSString(const S: string): NString; begin Result := TNSString.Wrap(DelphiToCFString(S)); end; function NSToDelphiString(const S: NSString): string; begin Result := CFToDelphiString((S as ILocalObject).GetObjectID); end;
If truth be told, using the Core APIs is a little tedious – being able to pass in C-style ‘strings’ and ‘arrays’ directly would be so much simpler, but no, everything is done through pseudo-object wrappers. However, the tedium applies regardless of the language you are programming in – it isn’t a special Delphi ‘tax’.