Objective-C vs C vs C++ vs Swift: A Comparative Map

Learn to place Objective-C accurately among its three closest relatives — C, C++, and Swift — by walking through a feature-by-feature comparison table covering procedural support, OOP model, dynamic runtime, message sending, memory management (ARC), pointer discipline, Apple API interoperability, and header-based API declaration, understanding both what distinguishes Objective-C and why each difference exists.

By this point in the course you have written a great deal of Objective-C: you have declared classes with @interface and @implementation, sent messages with square brackets, watched ARC insert retains and releases you never had to write by hand, and reached into C directly whenever it was the right tool. What you have not done yet is step back and see Objective-C in relation to the languages it sits between. Objective-C is, quite literally, a superset of C — every valid C program is a valid Objective-C program — and it shares a runtime lineage with Swift on Apple's platforms while sharing an 'object-oriented layer bolted onto C' ancestry with C++. Understanding exactly where Objective-C agrees with each of these languages and exactly where it diverges is one of the fastest ways to consolidate everything this course has taught, because every divergence you will look at in this lesson is something you have already met concretely in an earlier module. This lesson works through a compact comparison table drawn across eight features and four languages — Procedural, OOP, Dynamic runtime, Message sending, ARC, Pointers, Apple API interoperability, and Header-based APIs — taking each row in turn and grounding it in specifics you have already learned, before closing with an important caveat about what a table like this can and cannot tell you. As you read each row below, resist treating 'Yes' and 'No' as quality judgments: 'No built-in OOP' for C is not a criticism, and 'Limited' for Swift's procedural support reflects a design philosophy, not a deficiency. Read every cell as 'here is the shape of this language's design,' not as a scoreboard.

Procedural: All Four, With Swift the Outlier

C is procedural to its core and has essentially nothing else on offer at the language level — functions, control flow, and data laid out explicitly in memory are the entire toolkit, and that is the language's whole design. Objective-C inherits this completely: because it is a strict superset of C, every procedural technique you would write in plain C — loops, functions, structs, switch statements, pointer arithmetic — is valid, idiomatic Objective-C too, and in fact a large fraction of any real Objective-C codebase, including most method bodies, is ordinary procedural C code that simply happens to live inside an @implementation block and occasionally sends a message.

C++ is procedural in the same direct sense as C, since it too is (with some caveats) a superset of C, and plenty of real C++ code is written in a purely procedural style, especially in performance-sensitive inner loops where the overhead or indirection of virtual dispatch is deliberately avoided. What earns Swift the 'Limited' rating in this row is not that Swift lacks procedural constructs — it has functions, loops, and conditionals just like the other three — but that idiomatic Swift code is steered hard toward value types (structs and enums), protocols, and expression-oriented patterns like map/filter/reduce, so writing genuinely procedural, C-style top-to-bottom imperative code in Swift, while entirely possible, works against the grain of the language rather than with it.

The practical takeaway is that procedural competence transfers almost for free between C, Objective-C, and C++ — a for-loop or a function signature you wrote in your earliest C lessons in this course looks nearly identical in all three languages — while Swift asks you to unlearn some of that reflex and reach for its own idioms instead, even though the raw procedural capability is still sitting there if you need it.

Object-Oriented Programming: One Real Absence, Three Different Presences

C has no built-in object-oriented programming at all, and this is the single cleanest 'No' in the entire table. There is no class keyword, no inheritance, no polymorphism, no encapsulation enforced by the language. What C programmers have historically done instead — and what this course covered explicitly when it discussed simulating OOP in C — is fake it by hand: a struct holds the 'object's' data fields, and a table of function pointers embedded in or alongside that struct stands in for its 'methods,' with the first argument to each function conventionally being a pointer to the struct itself, playing the role that self or this plays in a real object system. It works, and large real systems (including parts of the Objective-C and Swift runtimes themselves) are built this way, but it is a convention enforced by programmer discipline, not a guarantee enforced by the compiler.

Objective-C, C++, and Swift all have genuine, compiler-supported OOP, but by three different mechanisms, and the differences matter more than the shared 'Yes' suggests. Objective-C's object model is dynamic and message-based: every method call is, under the hood, a call to a runtime function (objc_msgSend) that looks up the right implementation at the moment the message is sent, which is why Objective-C supports things like swapping a method's implementation at runtime or forwarding a message to an object that never declared it. C++ uses static, compile-time-resolved dispatch for ordinary calls and vtable-based dynamic dispatch specifically for virtual methods, where each object with virtual functions carries a hidden pointer to a per-class table of function pointers that gets consulted at the call site — dynamic, but resolved through a fixed table laid out at compile time, not through a general-purpose runtime lookup. Swift blends the two philosophies: classes use a vtable-like dispatch mechanism broadly similar in spirit to C++'s, while protocols — Swift's structural, composition-first alternative to classical inheritance — use a separate mechanism called witness tables to dispatch calls on types that conform to them, giving Swift both a class-based OOP model and a protocol-oriented model living side by side.

So while the table's OOP row looks like three 'Yes' cells and one 'No,' the real story is that Objective-C's dynamic message dispatch, C++'s static-typed classes with vtable-based virtual dispatch, and Swift's dual class-plus-protocol system are three meaningfully different answers to 'how do I get polymorphism,' and understanding which mechanism you are relying on in a given language changes what tricks are and are not available to you — Objective-C's runtime tricks like method swizzling, for instance, depend directly on its dynamic dispatch model and have no equivalent in C++'s compile-time vtable layout.

Dynamic Runtime: Objective-C's Defining Feature

This row is where Objective-C stands furthest apart from its three neighbors, and it is worth returning directly to the Objective-C Runtime lesson earlier in this course to reconnect the dots. C has no runtime in this sense at all — a compiled C program is, functionally, just machine instructions and data with no layer inspecting or manipulating its own structure while it runs; there is no built-in way to ask 'what methods does this type have' because C has no concept of a type having methods in the first place.

Objective-C's runtime, in sharp contrast, is a genuine C library (libobjc) linked into every Objective-C program, and it is present and active the entire time your program runs, not just at compile time. It is what makes objc_msgSend work, it is what backs introspection functions like respondsToSelector: and isKindOfClass:, it is what lets you add methods to a class at runtime, swap one method's implementation for another's, and even define entirely new classes dynamically — all capabilities this course covered directly when it introduced the runtime as its own module. This is a fundamentally different kind of dynamism than 'the program can make decisions based on data it reads' — it is the program's own structure (its classes, methods, and message dispatch) being open to inspection and modification while the program executes.

C++ occupies a middle ground the table labels 'Limited': it offers RTTI (Run-Time Type Information) through mechanisms like typeid and dynamic_cast, which let code ask limited questions about an object's actual runtime type, but this is a narrow, opt-in facility bolted onto an otherwise statically-resolved type system, nothing like Objective-C's always-on, pervasively dynamic runtime. Swift, likewise rated 'Limited,' offers some reflection through types like Mirror, but Swift's reflection is deliberately restricted and mostly read-only compared to Objective-C — you can inspect a Swift value's structure to a degree, but you cannot swap out a method implementation at runtime the way you can with the Objective-C runtime, because Swift's performance and safety guarantees depend on far more being locked down at compile time.

Message Sending: The Feature Unique to Objective-C

No other language in this table does what Objective-C does here, and it deserves to be called out plainly: only Objective-C uses true message sending in the [receiver selector] sense this course covered in its Sending Messages lesson. When you write [myObject doSomething], you are not making a direct function call to a fixed address the way C invokes a function; you are constructing a message — a receiver, a selector, and any arguments — and handing it to the objc_msgSend runtime function, which looks up the appropriate method implementation for that receiver's actual class at that moment and only then jumps to it. This is why Objective-C can meaningfully answer 'does this object respond to this selector' before ever calling it, and why message forwarding — redirecting a message an object doesn't implement to some other object entirely — is possible at all: there is a genuine dispatch step in between the call site and the method body for the runtime to intervene in.

C has nothing resembling this: a call to a C function is resolved to a fixed address (or at most, through a function pointer you set up yourself) with no dispatch machinery involved. C++'s virtual dispatch, discussed in the OOP row above, is dynamic in the sense that the actual function invoked depends on the object's runtime type, but it works by indexing into a vtable that was laid out at compile time — the compiler already knows the table's shape, and there is no equivalent of a selector being looked up by name or a message being forwarded to an unrelated object at runtime. Swift's dispatch for protocol conformances similarly goes through witness tables assembled largely at compile time (with some flexibility for existentials), and its class dispatch is vtable-based like C++'s; neither involves anything like a named selector being resolved dynamically against an arbitrary receiver the way objc_msgSend does.

The practical consequence, which you have already seen concretely in this course's coverage of selectors and dynamic messaging, is that patterns like KVO, target-action, and delegate-based callbacks throughout Cocoa lean directly on this message-sending model — a selector can be stored as a first-class value (a SEL) and handed around, sent later to whatever object happens to be listening, in a way that has no direct equivalent in C, C++, or Swift's own native dispatch mechanisms.

ARC: Three Different Memory Stories, One Shared Name

Memory management is where the table's four cells look almost symmetric — 'No', 'Yes', 'No', 'Swift ARC' — but that symmetry hides an important distinction the spec is careful to call out explicitly: Swift's ARC and Objective-C's ARC are conceptually similar but genuinely separate implementations, not the same system shared across two languages. C, as this course's earliest modules established, has no ARC at all: every malloc must be paired with a free by the programmer, and forgetting is a leak while calling free twice or too early is a crash or worse. This is the baseline every other row in this section is a response to.

Objective-C's ARC, covered in this course's dedicated ARC lesson, is a compile-time feature: the compiler inserts the retain, release, and autorelease calls for you by statically analyzing ownership at every assignment, method return, and property access, following strict-but-learnable rules (strong, weak, unsafe_unretained, copy) rather than doing anything at runtime to trace object graphs. C++, notably, has no ARC of any kind — the language's answer to the same underlying problem of 'who cleans this up' is RAII (Resource Acquisition Is Initialization) paired with smart pointers like std::unique_ptr and std::shared_ptr, where a wrapper object's own destructor — called deterministically when it goes out of scope — is responsible for releasing whatever it owns. RAII is philosophically related to ARC (both aim to make manual free/release calls unnecessary in ordinary code) but is a fundamentally different mechanism: no compiler-inserted retain/release traffic, just destructors running at well-defined scope-exit points.

Swift's ARC, the fourth cell, is the one most likely to be misunderstood: because it shares Objective-C's name, acronym, and even similar strong/weak/unowned terminology, it is tempting to assume Swift simply reuses Objective-C's ARC underneath. It does not. Swift has its own, separate ARC implementation, tuned for Swift's own value-type/reference-type distinction, its own optional-heavy ownership model, and its own runtime — the two ARCs happen to interoperate reasonably smoothly when an Objective-C object crosses into Swift code or vice versa (an important practical fact for anyone doing mixed-language development on Apple platforms) but they are not literally the same piece of code, and treating them as identical will eventually lead you astray on details like exactly when a deinit or dealloc fires relative to the other language's equivalent.

Pointers: Shared Power, Divergent Safety Philosophy

C, Objective-C, and C++ all give you raw, largely unrestricted pointers, and this is a direct consequence of Objective-C's and (mostly) C++'s status as C-derived languages: a pointer in all three is, at bottom, just an address in memory, and the language will generally let you do arithmetic on it, cast it to an unrelated type, dereference it past the end of an allocation, or leave it dangling after the memory it pointed to has been freed, all without stopping you. This is the same raw-pointer discipline this course covered from the very first C modules — powerful, close to the machine, and entirely dependent on the programmer getting it right, because the language itself offers few guardrails.

Swift's pointer story is deliberately different, and the table's 'Restricted' rating for Swift reflects a genuine design philosophy rather than a missing feature. Ordinary Swift code almost never touches a raw pointer at all — references to class instances are managed automatically by Swift's ARC, arrays and dictionaries are value types with safe, bounds-checked access, and optionals (rather than nullable pointers) are how Swift represents 'might not have a value.' Swift does expose raw pointer types (UnsafePointer, UnsafeMutablePointer, and friends) for the specific cases where you genuinely need one — interoperating with a C API being the most common — but reaching for them is opt-in and the word 'Unsafe' is baked directly into the type names as a deliberate, constant reminder that you have stepped outside the language's usual safety guarantees.

This difference is not cosmetic. It is the clearest single expression of a broader philosophical split running through the whole table: C, Objective-C, and C++ trust the programmer with the machine's raw capabilities and accept that trust will sometimes be misplaced, while Swift is designed to make an entire large category of memory-safety bugs — dangling pointers, buffer overruns, use-after-free — difficult to write by accident, at the cost of making the small remaining set of cases where you truly need raw memory access more visibly marked and more deliberately opted into.

Apple API Interoperability: C at the Floor, Objective-C at Home, C++ and Swift by Bridge

All four languages can talk to Apple's platform APIs, but at very different levels of directness, and this row is really about how naturally each language sits on top of the operating system's actual API surface. C interfaces at the lowest level: many of the true system-level APIs on Apple's platforms — POSIX calls, low-level I/O, parts of the kernel interface — are C APIs at their core, and every other language on this list ultimately has to be able to call down into C one way or another to reach them. C gets there directly, with no bridge required, because it is already speaking the API's native tongue.

Objective-C's interoperability is excellent for a much more specific and historically important reason: a huge fraction of Apple's own frameworks — Foundation, UIKit, AppKit, and a long list of others this course has drawn on throughout — are themselves written in or expose their public surface as Objective-C, which means calling them from Objective-C is not 'interoperability' in the bridging sense at all; it is simply calling code written in your own language. This is a major part of why Objective-C is the natural, native fit on Apple's platforms that this course has treated it as throughout: NSString, NSArray, UIView, and the rest were designed as Objective-C classes from the outset.

C++ reaches these same Objective-C frameworks specifically through Objective-C++, the hybrid dialect this course covered in its own dedicated lesson, which lets a single .mm file mix genuine C++ and genuine Objective-C syntax and pass objects between the two type systems within that file. It is a real, well-supported bridge, but it is a bridge — C++ does not speak to UIKit natively the way Objective-C does; it speaks to UIKit by embedding Objective-C alongside it. Swift's interoperability, meanwhile, is first-class and largely automatic thanks to the Clang Importer, the bridging system covered in this course's dedicated Swift Interoperability lesson, which reads Objective-C headers directly and synthesizes matching Swift-native declarations, so that calling into Foundation or UIKit from Swift looks like calling ordinary Swift APIs even though the underlying framework code is still Objective-C under the hood — a deliberately smoother, more automatic bridge than what Objective-C++ provides for C++.

Header-Based APIs: The Line Swift Draws

C, Objective-C, and C++ all declare their public API surface the traditional way: a header file (.h) listing function signatures, class or struct declarations, and constants, separate from the implementation file that actually defines them, and included by any other file that wants to call into that code. This course's own Header Files lesson covered exactly this mechanism for Objective-C specifically — an @interface block in a .h file, with the corresponding @implementation living in a separate .m file — and the same basic split-file, textually-included convention goes back to C itself and carries through essentially unchanged into C++.

Swift breaks with this convention entirely, and the table's 'No traditional headers' note for Swift is not a minor footnote — it reflects a genuinely different build and API-declaration model. A Swift module has no header file a consuming file #includes or #imports the way Objective-C or C++ code does; instead, the Swift compiler generates a module interface directly from the compiled module itself, and other Swift code that imports that module reads its public API from that generated interface rather than from any hand-written declaration file. There is nothing for you to maintain in parallel the way an Objective-C programmer has to keep a .h file's declarations honest against the .m file's actual implementation.

This has a real, practical consequence when Objective-C and Swift code coexist in the same project, which this course's Swift Interoperability lesson covers in more depth: Objective-C code exposed to Swift still needs a conventional header (or, more precisely, needs its declarations visible through Objective-C's normal header mechanism) for the Clang Importer discussed in the previous section to read and translate, while Swift code exposed back to Objective-C is instead surfaced through an auto-generated Objective-C-compatible header that Xcode produces for you, meaning a Swift file itself never needs a header of its own even when Objective-C code needs to call into it.

The Caveat: This Table Is Conceptual, Not a Specification

Every row above has tried to add texture the bare table cannot carry on its own, and that is the point being made explicitly here: the table you have just walked through is a conceptual teaching aid, a simplified orientation, not an exhaustive or authoritative specification of any of these four languages. A single word like 'Yes,' 'No,' or 'Limited' in one cell is doing an enormous amount of compression — it is a starting point for understanding, useful precisely because it is short enough to hold in your head at once, but it necessarily leaves out qualifications, edge cases, and entire sub-features that a real comparison would need to address.

Each of these four languages has considerably more nuance and additional capability than eight rows can capture. C++ alone has moved through many standards revisions (C++11 through the most recent), each adding capabilities — move semantics, constexpr evaluation, concepts, coroutines — that don't fit cleanly into any single cell of a table like this one. Swift's type system includes generics, opaque types, and result builders with no real counterpart anywhere in this table at all. Objective-C's runtime dynamism goes considerably deeper than the single 'Dynamic runtime: Yes' cell suggests, encompassing associated objects, method swizzling, and dynamic subclassing, several of which this course covered as their own dedicated lessons. None of that nuance is a flaw in the table; it is simply outside what a table of this shape is for.

Treat this comparison the way you would treat a map rather than the territory itself: it orients you quickly, it tells you roughly where things are relative to one another, and it is genuinely useful for exactly that purpose — but if you need to actually navigate a specific corner of any of these four languages in real depth, you go back to that language's own dedicated lessons, documentation, and specification, the way this course's other modules have done for Objective-C throughout. A table is where a comparison starts, never where real understanding of any one of these four languages ends.

Feature Comparison Table

This table is conceptual and simplified — a teaching orientation, not an authoritative specification of any of these four languages. Each language has considerably more nuance and additional capabilities than a single row per feature can capture; treat the table as a starting map, not a final word. 'C++ via Obj-C++' refers specifically to Objective-C++ (.mm files), the hybrid dialect covered in its own dedicated lesson in this course.

Feature                       | C            | Objective-C  | C++                | Swift
-------------------------------|--------------|--------------|---------------------|---------------------
Procedural                    | Yes          | Yes          | Yes                 | Limited
OOP                           | No built-in  | Yes          | Yes                 | Yes
Dynamic runtime               | No           | Yes          | Limited             | Limited
Message sending                | No           | Yes          | No                  | No
ARC                           | No           | Yes          | No                  | Swift ARC
Pointers                      | Yes          | Yes          | Yes                 | Restricted
Apple API interoperability     | C level      | Excellent    | Excellent via Obj-C++ | Excellent
Header-based APIs             | Yes          | Yes          | Yes                 | No traditional headers

Code Examples

This contrasts the Message sending row directly: only the top line is true Objective-C-style message sending, where [animal makeSound] is a receiver-plus-selector handed to objc_msgSend at runtime. The two comments below sketch, conceptually, how C's direct call and C++'s vtable dispatch solve the same 'call the right makeSound' problem through entirely different, non-message-based mechanisms.

[animal makeSound];

This lines up the ARC row's four cells side by side: Objective-C's compiler-inserted retain/release traffic, C's fully manual malloc/free discipline with zero safety net, and a sketch of how Swift's own separate ARC implementation handles an analogous case. The key point reinforced here is that Objective-C ARC and Swift ARC are conceptually related but genuinely distinct systems, not one shared implementation.

NSMutableArray *list = [NSMutableArray array];

This example makes the Pointers row concrete: the Objective-C/C code advances a raw pointer past its allocation with no compiler objection whatsoever, exactly as C++ would behave in the same situation, while the sketched Swift equivalent shows that reaching the same low-level capability in Swift requires deliberately opting into an Unsafe-prefixed API rather than being the default, ordinary way of working with a collection.

cursor += 10; // undefined behavior -- the language will not stop you

Common mistake: Assuming Swift simply lacks pointers entirely — it has them, but access is restricted and opt-in rather than the unrestricted default C, Objective-C, and C++ all share.

// Objective-C: dynamic message send
// The runtime looks up -(void)makeSound at the moment this line runs,
// based on [animal class] -- which might be Dog, Cat, or a subclass
// neither you nor the compiler had ever heard of when this line was written.
[animal makeSound];

// Conceptually, what C does instead: a direct call to a fixed function,
// resolved once, at compile time, with no dispatch step in between.
// void makeSound(Animal *animal) { ... }
// makeSound(animal);

// Conceptually, what C++ virtual dispatch does instead: a lookup through
// a per-class vtable laid out at compile time -- dynamic, but not a
// general message send with a first-class selector like Objective-C's.
// animal->makeSound(); // resolved via animal's vtable pointer
// Objective-C with ARC: the compiler inserts retain/release for you.
NSMutableArray *list = [NSMutableArray array];
[list addObject:@"first"];
// No -release call needed here -- ARC handles it when `list`
// goes out of scope, based on compile-time ownership analysis.

// Conceptually, the C equivalent has no ARC at all:
// int *buffer = malloc(sizeof(int) * 10);
// ... use buffer ...
// free(buffer);   // YOU must remember this call, every single time.

// Swift's ARC handles the analogous case with its own, separate
// implementation -- conceptually similar to Objective-C's, but not
// literally the same mechanism under the hood:
// var list: [String] = []
// list.append("first") // Swift ARC manages any underlying reference types
// Objective-C / C: an unrestricted raw pointer.
// Nothing stops you from advancing it past the allocation's end.
int values[5] = {1, 2, 3, 4, 5};
int *cursor = values;
cursor += 10; // undefined behavior -- the language will not stop you

// Conceptually, C++ raw pointers behave identically for this example:
// int *cursor = values; cursor += 10; // also undefined behavior

// Conceptually, Swift requires explicitly opting into this danger:
// let values = [1, 2, 3, 4, 5]
// values.withUnsafeBufferPointer { buffer in
//     // 'Unsafe' is in the type name deliberately -- you must ask for this.
// }

Why This Comparison Matters on a Real Apple Team

Production iOS and macOS codebases frequently mix two or three of these languages in the same project: legacy Objective-C view controllers, a C or C++ core for performance-critical media or numerical code, and newer feature work written in Swift. An engineer who can correctly predict where each language's model will and will not match another's — that Objective-C's dynamic messages can be forwarded but a C++ virtual call cannot, that Swift's ARC and Objective-C's ARC interoperate but are not the same implementation, that raw pointers move freely between C, Objective-C, and C++ but require an explicit Unsafe-prefixed opt-in from Swift — avoids an entire category of integration bugs and unnecessary architectural detours that engineers who only know one of these languages well tend to hit at the seams.

Assuming Swift's ARC and Objective-C's ARC are literally the same system because they share the name and similar keywords.

Understand them as conceptually similar but genuinely separate implementations that happen to interoperate smoothly when Objective-C and Swift code work together.

The spec calls this out explicitly for a reason: treating the two ARCs as identical leads to wrong assumptions about deinitialization timing and ownership behavior at the language boundary.

Reading 'C++: No' in the Message sending row as meaning C++ has no dynamic dispatch at all.

Recognize that C++ has its own dynamic dispatch mechanism — vtable-based virtual functions — it simply is not Objective-C-style message sending with a first-class selector resolved by a runtime like objc_msgSend.

'No message sending' describes the absence of one specific mechanism, not the absence of polymorphism or dynamic behavior generally; C++ virtual dispatch is real, just architecturally different.

Treating the table's single word per cell as the complete truth about a language's capabilities.

Use the table as a simplified, conceptual starting orientation, and go back to each language's own dedicated lessons and documentation for real depth.

The spec explicitly frames this table as conceptual; each of the four languages has considerably more nuance than one row per feature can capture.

Assuming Objective-C++ makes C++ a fully native citizen of Apple's frameworks the same way Objective-C is.

Understand Objective-C++ as a genuine, well-supported bridge (mixing .mm-file C++ and Objective-C) rather than C++ natively speaking Objective-C frameworks the way Objective-C itself does.

Objective-C interoperates with Apple frameworks natively because those frameworks are largely written in Objective-C; C++ reaches them by embedding Objective-C alongside it in a hybrid file, which is a different kind of access.

According to the comparison table, which language is rated as having 'Limited' support for straightforward procedural-style code, despite fully supporting it? A. C B. Objective-C C. C++ D. Swift

D. Swift — Swift fully supports procedural code but its design pushes developers toward value types, protocols, and expression-oriented patterns instead, earning it the 'Limited' rating in that row.

Which language in the table has no built-in object-oriented programming at all? A. C B. Objective-C C. C++ D. Swift

A. C — C has no classes, inheritance, or polymorphism built into the language; OOP in C is simulated by hand using structs paired with function pointers.

What is the key architectural difference between Objective-C's dynamic dispatch and C++'s virtual function dispatch? A. C++ has no dynamic dispatch mechanism of any kind B. Objective-C resolves messages via a runtime lookup (objc_msgSend), while C++ resolves virtual calls through a per-class vtable laid out at compile time C. They are functionally identical mechanisms with different syntax D. C++'s dispatch is dynamic and Objective-C's is entirely static

B. Objective-C resolves messages via a runtime lookup (objc_msgSend), while C++ resolves virtual calls through a per-class vtable laid out at compile time — Objective-C sends a message to be resolved by the runtime at call time, enabling forwarding and introspection; C++ virtual dispatch indexes into a vtable whose shape the compiler already knows at compile time.

Why does the table describe Swift's ARC and Objective-C's ARC as related but distinct, rather than as the same system? A. Because Swift does not actually have ARC B. Because they are separate implementations that happen to interoperate, despite sharing similar terminology and concepts C. Because Objective-C's ARC only works on Swift code D. Because C++'s RAII is actually what powers both of them

B. Because they are separate implementations that happen to interoperate, despite sharing similar terminology and concepts — Swift has its own, separately implemented ARC, conceptually similar to Objective-C's compile-time-inserted retain/release model but not literally the same code, even though the two interoperate smoothly.

How does C++ typically achieve interoperability with Apple's Objective-C-based frameworks? A. C++ calls them natively with no bridge required, the same way Objective-C does B. Through Objective-C++ (.mm files), which mixes real C++ and real Objective-C syntax in one file C. C++ cannot interoperate with Objective-C frameworks at all D. Through the Clang Importer, the same mechanism Swift uses

B. Through Objective-C++ (.mm files), which mixes real C++ and real Objective-C syntax in one file — C++ reaches Objective-C frameworks specifically through Objective-C++, a hybrid dialect covered in its own dedicated lesson; the Clang Importer is Swift's bridging mechanism, not C++'s.

What does the table mean by saying Swift has 'No traditional headers'? A. Swift code cannot declare a public API at all B. Swift never interoperates with Objective-C code C. Swift generates a module interface from the compiled module itself instead of using a hand-maintained header file the way C, Objective-C, and C++ do D. Swift uses the exact same .h header files as Objective-C

C. Swift generates a module interface from the compiled module itself instead of using a hand-maintained header file the way C, Objective-C, and C++ do — Rather than a separate .h file a consumer includes or imports, Swift's public API is exposed through a module interface generated directly from the compiled module, with no parallel header file to maintain by hand.