Core Foundation
Understand Core Foundation as the C-based framework underlying much of Foundation, learn its core opaque reference types (CFTypeRef, CFStringRef, CFArrayRef, CFDictionaryRef, CFDataRef, CFNumberRef, CFRunLoopRef), master the Create Rule and Get Rule memory ownership conventions, and see how CFRetain/CFRelease, callbacks, and CF-to-Objective-C interoperability fit together.
Underneath Foundation's Objective-C classes -- NSString, NSArray, NSDictionary, and the rest -- sits Core Foundation, an older, pure-C framework that predates ARC entirely and manages its own memory by an explicit, manual convention that ARC does not automatically apply to. This lesson is a dedicated tour of that layer: what Core Foundation actually is, its central opaque reference types, the Create Rule and Get Rule that govern who owns a CF object at any given moment, and CFRetain/CFRelease, the manual memory management every CF object still requires today, even inside a fully ARC-compiled Objective-C file. The deep mechanics of moving values between Core Foundation and Objective-C -- __bridge, __bridge_transfer, and __bridge_retained -- belong to the sibling Toll-Free Bridging lesson; this lesson introduces that interoperability at a conceptual level and points there for the full treatment, while building on the Manual Reference Counting and ARC lessons' groundwork on ownership.
What Core Foundation Is, and Why It Still Matters
Core Foundation is a C-level framework, not an Objective-C one -- its types are plain C structs and functions, with no classes, no message sending, and no @interface/@implementation in sight. It predates Objective-C's modern object-oriented Foundation library, and much of Foundation is, in fact, built directly on top of it: many Foundation classes are, under the hood, thin Objective-C wrappers around an underlying Core Foundation type, which is exactly what makes toll-free bridging between the two possible at all.
Even in code that never writes a single CF-prefixed symbol directly, Core Foundation is often still running underneath -- inside system frameworks, inside performance-sensitive code paths, and anywhere a C API needs to hand data to or receive data from Objective-C code. Understanding it matters because its memory management rules are fundamentally different from ARC's, and mixing the two without understanding the boundary between them is a common source of leaks and crashes.
• Core Foundation is a pure C framework: opaque struct-based types and C functions, not classes or message sends. • Many Foundation classes are thin Objective-C wrappers around an underlying Core Foundation type. • Its memory management predates ARC entirely and follows different rules, covered throughout this lesson.
CFTypeRef: The Root of the Core Foundation Type Hierarchy
CFTypeRef is Core Foundation's answer to Objective-C's id: a fully generic reference type that can hold a reference to any Core Foundation object at all -- a CFStringRef, a CFArrayRef, a CFNumberRef, anything. Functions like CFRetain, CFRelease, CFGetTypeID, and CFEqual are written to accept CFTypeRef, which is what lets one set of generic memory-management and inspection functions work uniformly across every kind of CF object, rather than needing a separate retain/release function per type.
Where id in Objective-C is understood by both the compiler and the runtime as a genuine object pointer with class-based dispatch behind it, CFTypeRef is simply a generic opaque pointer type at the C level -- there is no message sending involved at all; every operation on a CFTypeRef value is an ordinary C function call, and which concrete CF type you actually have is something your code has to already know (or check with CFGetTypeID), not something the compiler enforces for you.
• CFTypeRef: Core Foundation's generic reference type -- roughly analogous to id, but for the C-level CF world. • Generic memory functions (CFRetain, CFRelease, CFEqual) accept CFTypeRef so they work across every CF type uniformly. • No message sending is involved -- every CF operation is an ordinary C function call, not dynamic dispatch.
The Core Foundation Value and Collection Types
CFStringRef is Core Foundation's string type, and it's the type most Foundation NSString instances are, underneath, toll-free bridged to -- immutable text data managed by CF's own rules rather than ARC's. CFArrayRef and its mutable counterpart CFMutableArrayRef hold ordered collections of CF (or, via bridging, Objective-C) values, configured at creation time with a callback structure that tells the array how to retain, release, compare, and hash whatever it stores.
CFDictionaryRef and CFMutableDictionaryRef provide key-value storage the same way NSDictionary/NSMutableDictionary do at the Objective-C level, again governed by callback structures for both keys and values. CFDataRef wraps a fixed block of raw bytes -- the CF-level counterpart to NSData -- and CFNumberRef wraps a single numeric value (an integer or floating-point type) as an opaque CF object, the counterpart to NSNumber.
Each of these types is created, retained, and released using the exact same generic conventions -- the Create Rule and CFRetain/CFRelease, both covered in the next two sections -- which is precisely the benefit of building the whole framework around the shared CFTypeRef root.
• CFStringRef: immutable text data -- most NSString instances are toll-free bridged to this underneath. • CFArrayRef / CFMutableArrayRef: ordered collections, configured with callbacks for retaining/releasing their elements. • CFDictionaryRef / CFMutableDictionaryRef: key-value storage, with separate callback structures for keys and values. • CFDataRef: a block of raw bytes, the CF counterpart to NSData. • CFNumberRef: a single numeric value wrapped as an opaque CF object, the CF counterpart to NSNumber.
The Create Rule and the Get Rule: Core Foundation's Two Ownership Conventions
Core Foundation has no ARC, and it never had automatic reference counting of any kind -- instead, it governs ownership through two clearly named conventions that every function in the framework follows consistently, and this distinction is one of the most commonly confused parts of working with CF, so it's worth stating precisely, with both rules side by side.
The Create Rule: any function whose name contains 'Create' or 'Copy' returns a CF object that the caller owns. Owning it means you are responsible for calling CFRelease on it exactly once when you're done -- CFStringCreateWithCString, CFArrayCreateMutable, and CFDictionaryCopyMultiple are all Create Rule functions, and every single one of them hands you a reference you must eventually release yourself.
The Get Rule: any function whose name contains 'Get' (and, more generally, most functions that do not contain 'Create' or 'Copy') returns a CF object the caller does not own. CFDictionaryGetValue and CFArrayGetValueAtIndex are Get Rule functions -- they hand you a reference to something already owned by the collection you got it from, valid only as long as that owner keeps it alive, and you must not call CFRelease on it yourself, because doing so would release an object you never actually owned a claim to, unbalancing the real owner's reference count.
• Create Rule: function name contains 'Create' or 'Copy' -- caller owns the result and must CFRelease it exactly once. • Get Rule: function name contains 'Get' (or otherwise doesn't create/copy) -- caller does not own the result and must not release it. • This naming convention is deliberate and consistent across the entire Core Foundation API -- the function name itself tells you which rule applies. • Releasing a Get Rule result, or forgetting to release a Create Rule result, are the two classic Core Foundation memory bugs.
CFRetain and CFRelease: Manual Memory Management That Predates ARC
CFRetain increments a CF object's reference count, and CFRelease decrements it, deallocating the object once the count reaches zero -- functionally the same idea as Objective-C's manual retain/release, as covered in the Manual Reference Counting lesson, but implemented at the C level for CF's opaque struct-based objects rather than for Objective-C objects.
The detail that trips people up most often: ARC does not manage plain CFTypeRef-family variables at all, even inside a .m file compiled with ARC fully enabled. ARC's automatic retain/release insertion applies to Objective-C object pointers -- variables with an ownership qualifier like __strong -- and a bare CFStringRef or CFArrayRef variable simply isn't one of those; it's a C pointer as far as ARC is concerned. That means every CFRetain your code needs must still be written by hand, and every matching CFRelease must still be written by hand, in exactly balanced pairs, precisely the discipline the Manual Reference Counting lesson taught for pre-ARC Objective-C code. The only way a CF value gets ARC's automatic help is by crossing over into genuine Objective-C object management via toll-free bridging, which the sibling Toll-Free Bridging lesson covers in depth.
• CFRetain: increments a CF object's manual reference count. • CFRelease: decrements it, deallocating the object once the count reaches zero. • ARC does NOT automatically manage plain CFTypeRef-family variables, even in an otherwise fully ARC-compiled file. • Every CFRetain/CFRelease must still be written by hand in balanced pairs, unless the value is bridged to ARC-managed Objective-C memory.
Callbacks and Opaque CF Objects
Core Foundation objects are opaque: their internal struct layout is intentionally hidden from your code, exposed only through the functions the framework provides -- you never reach directly into a CFArrayRef's internals the way you might with a plain C struct. This opacity is what lets Apple change a CF type's actual implementation across releases without breaking code that only ever interacts with it through the published functions.
Because collection types like CFArrayRef and CFDictionaryRef can hold arbitrary kinds of values -- CF objects, Objective-C objects, or even plain C data -- they need to be told, at creation time, how to manage whatever you put into them: how to retain a value when it's added, release it when it's removed, compare two values for equality, and compute a hash. That's exactly what a callback structure like kCFTypeArrayCallBacks provides: a bundle of C function pointers the collection calls internally whenever it needs to perform one of those operations on its contents, which is what allows a CFMutableArrayRef created with kCFTypeArrayCallBacks to correctly retain and release CF objects (including toll-free bridged Objective-C objects) added to and removed from it automatically.
• CF objects are opaque -- their internal layout is hidden, accessible only through the framework's own functions. • Collections need callback structures (like kCFTypeArrayCallBacks) to know how to retain/release/compare/hash their contents. • Without proper callbacks, a mutable CF collection just stores raw pointers with no memory management of its contents at all.
CFRunLoopRef and Event-Driven Programming
CFRunLoopRef represents a run loop: the mechanism that keeps a thread alive and responsive, repeatedly waiting for and dispatching events -- timers firing, input sources becoming ready, and other asynchronous work -- rather than running straight through a fixed sequence of statements and exiting. Every thread that needs to stay around and respond to events over time, most visibly a UI application's main thread, is driven by a run loop underneath.
A run loop doesn't do anything on its own until you add sources to it -- timers via CFRunLoopAddTimer, or other event sources -- and then actually run it, with a function like CFRunLoopRunInMode:, which blocks the current thread, dispatching callbacks as their associated events fire, for as long as the run loop keeps finding work to do (or until an explicit time limit is reached). This is the mechanical foundation underneath event-driven programming in Objective-C: without a run loop actually spinning, scheduled timers, network callbacks, and UI events simply never get dispatched, no matter how correctly they were configured.
• CFRunLoopRef: the run loop that keeps a thread alive and dispatches events -- timers, input sources, and more. • Nothing fires until sources are added to the run loop and the run loop is actually run (e.g. CFRunLoopRunInMode). • The mechanical basis of event-driven programming: without a spinning run loop, scheduled work is never dispatched.
Core Foundation and Objective-C Interoperability
Because many Foundation classes are, underneath, thin wrappers around a corresponding Core Foundation type, it's possible to move a single piece of data between the CF world and the Objective-C world without copying it -- an NSString and the CFStringRef it's built on can, in the right circumstances, be treated as literally the same underlying object, viewed through two different type systems. This capability is called toll-free bridging, and it's genuinely powerful: code can call a CF-only API on data that originated as an Objective-C object, or hand a CF-created value straight to Objective-C code expecting an NSString *, without any actual data copying taking place.
The mechanics of doing this correctly -- the __bridge, __bridge_transfer, and __bridge_retained cast keywords, and exactly how each one interacts with ARC's automatic management versus Core Foundation's manual CFRetain/CFRelease -- are covered in full in the sibling Toll-Free Bridging lesson. What matters here is the conceptual shape: crossing from Core Foundation into ARC-managed Objective-C memory (or back) is a deliberate, explicit operation, precisely because the two sides have fundamentally different memory management rules, as this lesson's earlier sections on the Create Rule, the Get Rule, and manual CFRetain/CFRelease should make clear.
• Toll-free bridging: an NSString and its underlying CFStringRef can be the same object, viewed through two type systems. • It allows CF-only APIs to be used on Objective-C-originated data, and vice versa, without copying. • The full mechanics -- __bridge, __bridge_transfer, __bridge_retained -- are covered in depth in the Toll-Free Bridging lesson.
Code Examples
CFStringCreateWithCString has 'Create' in its name, so the Create Rule applies: the caller owns the returned CFStringRef and is responsible for calling CFRelease on it exactly once, whether or not the code was compiled with ARC -- ARC does not manage plain CFStringRef variables automatically.
CFPreferencesCopyMultiple follows the Create Rule (note 'Copy' in the name), so settings must be released once by the caller. CFDictionaryGetValue follows the Get Rule: the value it returns is owned by the dictionary itself, valid only as long as settings is kept alive, and releasing it directly would incorrectly unbalance the dictionary's own internal ownership of it.
const void *value = CFDictionaryGetValue(settings, CFSTR("username"));
kCFTypeArrayCallBacks tells the array to retain and release its elements like ordinary CF objects, which is why it's safe to CFRelease ten immediately after appending it -- the array now holds its own retained reference. The CFRetain/CFRelease pair around 'numbers' demonstrates manual reference counting that predates ARC and still must be managed entirely by hand for plain CFTypeRef values, even in an ARC-compiled file.
CFRunLoopTimerCreate follows the Create Rule, so timer is released once it has been safely added to the run loop (which retains it independently). Nothing actually happens until CFRunLoopRunInMode is called -- that call is what makes the run loop start dispatching events, including the timer's fire callback, which is the mechanical basis of event-driven programming.
CFRunLoopRunInMode(kCFRunLoopDefaultMode, 5.0, false);
This is a conceptual preview only: __bridge crosses the CF/Objective-C boundary without transferring ownership (whoever owned it before still owns it), while __bridge_transfer hands ownership over to ARC, which is why created must not also be manually CFReleased afterward -- doing so would release an object ARC now believes it owns. The full rules, including __bridge_retained, are the entire subject of the Toll-Free Bridging lesson.
CFStringRef cfString = (__bridge CFStringRef)objcString;
CFStringRef greeting = CFStringCreateWithCString(kCFAllocatorDefault,
"Hello, Core Foundation",
kCFStringEncodingUTF8);
CFShow(greeting); // debug-print the CFString to the console
CFRelease(greeting); // required: "Create" is in the function name,
// so the caller owns this and must release itCFDictionaryRef settings = CFPreferencesCopyMultiple(NULL,
CFSTR("com.example.app"),
kCFPreferencesCurrentUser,
kCFPreferencesAnyHost);
// "Copy" is in the name -> Create Rule applies -> caller owns 'settings'
// and must CFRelease it.
const void *value = CFDictionaryGetValue(settings, CFSTR("username"));
// "Get" is in the name -> Get Rule applies -> caller does NOT own the
// returned value and must never CFRelease it directly.
CFRelease(settings); // balances the Copy above; releasing 'settings'
// is sufficient -- its contents are managed
// by the dictionary's own callbacksCFMutableArrayRef numbers = CFArrayCreateMutable(kCFAllocatorDefault,
0,
&kCFTypeArrayCallBacks);
int rawValue = 10;
CFNumberRef ten = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &rawValue);
CFArrayAppendValue(numbers, ten);
CFRelease(ten); // the array retained 'ten' itself via
// kCFTypeArrayCallBacks when it was appended
CFRetain(numbers); // e.g. handing a second, independent reference
// to some other piece of code that will
// release it on its own schedule
// ... later, in that other code ...
CFRelease(numbers); // balances the CFRetain above
CFRelease(numbers); // balances the original CFArrayCreateMutableCFRunLoopRef runLoop = CFRunLoopGetCurrent();
CFRunLoopTimerRef timer = CFRunLoopTimerCreate(
kCFAllocatorDefault,
CFAbsoluteTimeGetCurrent() + 1.0,
0, 0, 0,
^(CFRunLoopTimerRef t) {
NSLog(@"Timer fired");
},
NULL);
CFRunLoopAddTimer(runLoop, timer, kCFRunLoopDefaultMode);
CFRelease(timer); // the run loop retains the timer once it's added
// Actually drives event dispatch -- without this, the timer above
// would never fire, no matter how correctly it was configured
CFRunLoopRunInMode(kCFRunLoopDefaultMode, 5.0, false);NSString *objcString = @"Hello";
CFStringRef cfString = (__bridge CFStringRef)objcString;
// __bridge: no ownership transfer at all -- ARC still owns objcString,
// and cfString must not be CFReleased separately.
CFStringRef created = CFStringCreateWithCString(kCFAllocatorDefault,
"World",
kCFStringEncodingUTF8);
NSString *transferred = (__bridge_transfer NSString *)created;
// __bridge_transfer: ownership passes to ARC -- 'created' must NOT be
// CFReleased afterward; ARC will release 'transferred' when it's done.Objective-C and Core Foundation
Objective-C classes like NSString and NSArray sit above the corresponding Core Foundation types; toll-free bridging is what lets a single object be viewed through either type system without copying.
CFDictionaryRef settings = CFPreferencesCopyMultiple(...); // ... use settings ... // (settings is simply never released)
CFDictionaryRef settings = CFPreferencesCopyMultiple(...); // ... use settings ... CFRelease(settings);
CFPreferencesCopyMultiple follows the Create Rule ('Copy' in the name), so the caller owns the result and must call CFRelease exactly once. Forgetting to do so leaks the object, since ARC does not manage plain CFDictionaryRef variables automatically.
const void *value = CFDictionaryGetValue(settings, CFSTR("username")); CFRelease(value); // releasing a Get Rule result const void *value = CFDictionaryGetValue(settings, CFSTR("username")); // no release here -- 'value' is owned by 'settings', not by this code
CFDictionaryGetValue follows the Get Rule: the caller does not own the returned value, which remains owned by the dictionary it came from. Releasing it directly unbalances the dictionary's own retain count on that value and can cause a crash or corruption later.
CFStringRef cfString = CFStringCreateWithCString(kCFAllocatorDefault, "data", kCFStringEncodingUTF8); // treating this like an ARC-managed Objective-C object and letting it // simply fall out of scope with no CFRelease
CFStringRef cfString = CFStringCreateWithCString(kCFAllocatorDefault, "data", kCFStringEncodingUTF8); // ... use cfString ... CFRelease(cfString);
Even inside a fully ARC-enabled file, ARC does not manage plain CFTypeRef-family variables -- they are not Objective-C object pointers with an ownership qualifier as far as the compiler is concerned. Every Create Rule CF object still requires a manual, explicit CFRelease.
What is CFTypeRef, and how does it relate to id in Objective-C? A. CFTypeRef is Core Foundation's generic opaque reference type, roughly analogous to id, that generic functions like CFRetain and CFRelease operate on across every CF type B. CFTypeRef is a synonym for id -- they are the exact same type under a different name C. CFTypeRef can only refer to CFStringRef objects, never other CF types D. CFTypeRef performs dynamic message dispatch the same way id does
A. CFTypeRef is Core Foundation's generic opaque reference type, roughly analogous to id, that generic functions like CFRetain and CFRelease operate on across every CF type — CFTypeRef is Core Foundation's generic reference type, playing a role similar to id at the C level -- it lets generic functions like CFRetain, CFRelease, and CFEqual operate uniformly across every kind of CF object, but unlike id, it involves no message sending or dynamic dispatch at all.
According to the Create Rule, which of these function calls hands ownership of its return value to the caller, requiring a matching CFRelease? A. CFDictionaryGetValue(dict, key) B. CFArrayGetValueAtIndex(array, 0) C. CFStringCreateWithCString(allocator, cString, encoding) D. CFRunLoopGetCurrent()
C. CFStringCreateWithCString(allocator, cString, encoding) — CFStringCreateWithCString has 'Create' in its name, so the Create Rule applies: the caller owns the result and must CFRelease it. The other three follow the Get Rule -- they return values owned by something else, which the caller must not release directly.
Under the Get Rule, what is the caller's responsibility for a value returned by a function like CFDictionaryGetValue? A. The caller must call CFRetain on it immediately or it will be deallocated B. The caller does not own the returned value and must not call CFRelease on it -- it remains owned by whatever container returned it C. The caller owns it exactly like a Create Rule result and must release it once D. The caller must copy the value before using it in any way
B. The caller does not own the returned value and must not call CFRelease on it -- it remains owned by whatever container returned it — Get Rule functions -- typically containing 'Get' in the name -- return a reference the caller does not own. Releasing it directly would unbalance the actual owner's (e.g. the dictionary's) reference count on that value.
Why does a plain CFStringRef variable still require a manual CFRelease, even inside a .m file fully compiled with ARC enabled? A. ARC does manage it automatically, so the manual CFRelease is redundant but harmless B. ARC's automatic retain/release insertion applies to Objective-C object pointers with an ownership qualifier; a bare CFStringRef variable is not one of those, so ARC leaves its memory management entirely manual, predating and outside ARC's automation C. CFStringRef objects don't actually use reference counting at all D. CFRelease is only needed for CFMutableStringRef, never for CFStringRef
B. ARC's automatic retain/release insertion applies to Objective-C object pointers with an ownership qualifier; a bare CFStringRef variable is not one of those, so ARC leaves its memory management entirely manual, predating and outside ARC's automation — ARC only automates memory management for genuine Objective-C object pointer variables with an ownership qualifier. A plain CFTypeRef-family variable like CFStringRef is, as far as ARC is concerned, just a C pointer -- its CFRetain/CFRelease calls must still be written by hand in balanced pairs, exactly as manual reference counting required before ARC existed.
What role does CFRunLoopRef play in event-driven programming? A. It has nothing to do with events -- it is purely a data storage type like CFArrayRef B. It represents the run loop that keeps a thread alive and dispatches events like timers and input sources; nothing scheduled on it fires until the run loop is actually run C. It automatically retains and releases every CF object created on its thread D. It replaces CFRetain and CFRelease with automatic memory management
B. It represents the run loop that keeps a thread alive and dispatches events like timers and input sources; nothing scheduled on it fires until the run loop is actually run — CFRunLoopRef is the mechanism that keeps a thread alive and responsive to events over time. Adding a timer or other source to it configures what should eventually fire, but nothing actually dispatches until the run loop itself is run (e.g. via CFRunLoopRunInMode) -- that is the mechanical basis of event-driven programming.
What does toll-free bridging, previewed in this lesson and covered fully in the sibling Toll-Free Bridging lesson, allow? A. It lets a single underlying object be viewed through both the Core Foundation type system (e.g. CFStringRef) and the Objective-C type system (e.g. NSString *) without copying the data B. It automatically converts all CF memory management to ARC with no code changes C. It only works for CFNumberRef and NSNumber, no other CF/Foundation type pairs D. It eliminates the need for CFRetain and CFRelease entirely, for every CF type
A. It lets a single underlying object be viewed through both the Core Foundation type system (e.g. CFStringRef) and the Objective-C type system (e.g. NSString *) without copying the data — Toll-free bridging is the capability of treating one underlying object as either its CF type (like CFStringRef) or its corresponding Objective-C type (like NSString *) without copying data, because many Foundation classes are thin wrappers around a corresponding CF type. It does not eliminate CF's manual memory rules by itself -- crossing the boundary correctly (via __bridge, __bridge_transfer, or __bridge_retained) is what the Toll-Free Bridging lesson covers in full.
Objective-C
│
│ Toll-Free Bridging
↓
Core Foundation
│
├── CFString
├── CFArray
├── CFDictionary
├── CFData
└── CFRunLoop