Advanced Objective-C Type System

Go beyond the everyday object types covered in earlier lessons and build a precise mental model of Objective-C's full type system: id, instancetype, Class, SEL, IMP, BOOL, the platform-dependent numeric typedefs, fixed-width integers, __kindof, opaque and pointer-to-pointer types, ownership-qualified pointers, function and block pointers, nullability, and the distinction between a variable's compile-time type and an object's runtime type.

Most Objective-C lessons up to this point have treated types the way everyday code treats them: declare a variable with a class name, send it messages, move on. That's enough to write working programs, but it leaves a whole layer of the language unexamined -- the layer that explains why id and instancetype aren't interchangeable, why SEL and IMP are two completely different things even though both relate to method dispatch, why NSInteger silently changes size depending on what device the code runs on, and why a variable's declared type and the object actually sitting behind it in memory can, and often do, disagree. This lesson pulls all of that together into one systematic tour of Objective-C's type system, connecting back to the Objective-C Runtime, Nullability, Selectors, and performSelector lessons wherever their subject matter resurfaces here from a different angle.

Compile-Time Type vs. Runtime Type: The Idea That Ties Everything Together

Every other topic in this lesson is really a variation on one foundational idea, so it's worth stating first, on its own: in Objective-C, the type written in source code (the compile-time type) and the actual class of the object living in memory at a given moment (the runtime type) are two separate things, and the language is deliberately built to let them diverge. This is the same fact the Objective-C Runtime lesson introduced when it explained that method lookup happens against an object's real class, not its declared type -- this lesson generalizes that idea across the entire type system, not just method dispatch.

A variable declared NSObject *thing might, at runtime, actually point to a UIButton, a NSString, or any other class in the hierarchy -- the compiler only ever reasons about the declared type when it checks your code, while the runtime only ever reasons about the actual type when it looks up methods. Most of the time these two views agree closely enough that the difference is invisible. But a large number of Objective-C's more advanced type features -- id, __kindof, safe casting, isKindOfClass: -- exist specifically to manage the gap between what the compiler assumes and what is actually true when the program runs.

The diagram accompanying this lesson lays out this relationship as a chain: a piece of source code has a compile-time type, which is expressed as an Objective-C pointer type, which at runtime refers to an actual object, which belongs to some actual class. Every section below eventually connects back to some point on that chain.

• Compile-time type: what the source code declares a variable to be -- all the compiler ever checks against. • Runtime type: the actual class of the object sitting in memory when the program executes -- what the runtime's method lookup actually searches. • These two can (and often do) legitimately disagree; much of Objective-C's advanced type system exists to manage that gap safely. • This is the same principle the Objective-C Runtime lesson used to explain dynamic dispatch, generalized here to the whole type system.

id, Class, and instancetype: Objective-C's Generic Object Types

id is Objective-C's fully generic object pointer type: a variable declared id can point to an instance of any class at all, and the compiler performs essentially no compile-time type checking on messages sent to it -- it trusts that whatever object is actually there at runtime will respond to whatever selector you send. This is powerful (collections like NSArray store their elements as id internally) but it discards the safety net the compiler would otherwise provide; a typo'd selector sent to an id-typed variable is caught only at runtime, if at all.

Class is a distinct type representing a reference to a class itself, not an instance of that class -- the kind of value you get back from [NSString class] or from a class method's implicit self. It's what lets code ask 'what class is this?' and act on the answer, and it's the type behind patterns like [[someClass alloc] init] where someClass is a Class-typed variable holding a class to instantiate dynamically.

instancetype looks similar to id but solves a completely different problem: it tells the compiler that a method returns exactly the same type as the class it was called on, which id cannot express. A factory or init method declared to return instancetype and called on a subclass returns, as far as the compiler is concerned, an instance of that subclass -- no cast required, and the compiler still catches type errors on the result. Declaring the same method to return id would compile but would throw away that precision, forcing callers to cast manually to get subclass-specific type checking back.

• id: a fully generic object pointer -- any class, virtually no compile-time checking of the messages sent to it. • Class: a reference to a class itself (not an instance) -- what [SomeClass class] and a class method's self both are. • instancetype: return type meaning 'whatever type this method was actually invoked on' -- preserves precise typing that id would throw away. • Always prefer instancetype over id for init and factory method return types, exactly for this reason.

SEL and IMP: The Two Halves of Message Dispatch, as Types

The Selectors lesson introduced SEL as a runtime-registered identifier for a method name, obtained with @selector(...), and the performSelector lesson built on that to invoke methods dynamically by selector value. This lesson's job is to place SEL precisely inside the broader type system: a SEL value carries no code at all -- it is purely a name, a unique token the runtime uses to look something up. Two completely unrelated classes can each implement a method matching the same SEL value, and that SEL value by itself says nothing about which implementation will actually run for a given receiver.

IMP is SEL's counterpart, and the two are easy to conflate if you haven't seen them side by side: where SEL is just a name, IMP is a raw C function pointer to the actual block of executable code that implements a method for one specific class. Calling methodForSelector: performs the same method lookup the runtime does for an ordinary message send -- walking the receiver's class and its superclasses -- but instead of jumping to the result immediately, it hands that resulting function pointer back to you as an IMP, which you can then call directly, bypassing dispatch overhead on subsequent calls.

Put plainly: SEL answers the question 'which method, by name?' while IMP answers 'which specific code, for this specific class?' -- exactly the distinction the Objective-C Runtime lesson's method-lookup stage exists to resolve.

• SEL: a unique, runtime-registered identifier for a method name -- a name only, no code attached. • IMP: a raw C function pointer to the actual implementation code the runtime would jump to for a given SEL and class. • methodForSelector: performs method lookup once and returns an IMP you can call repeatedly without repeating that lookup. • Connects directly to the Selectors and performSelector lessons -- SEL is the name half, IMP is the code half.

BOOL, NSInteger, NSUInteger, CGFloat, and Fixed-Width Integers

BOOL is Objective-C's boolean type, with values YES and NO; historically it has been a typedef for signed char rather than C's native _Bool/bool, which is why comparing a BOOL directly against arbitrary integers (rather than treating any nonzero value as true) can produce surprising results in older code.

NSInteger and NSUInteger are platform-dependent integer typedefs, not fixed-size types: on a 64-bit platform NSInteger resolves to long (64 bits) and on a 32-bit platform it resolves to int (32 bits), with NSUInteger doing the same for the unsigned counterparts. They exist so that ordinary code -- array indices, counts, generic integer values -- automatically uses whatever integer width matches the platform's native pointer size, without every call site having to special-case 32-bit versus 64-bit builds. CGFloat follows the identical pattern for floating-point values: float on 32-bit platforms, double on 64-bit platforms.

Fixed-width integer types such as int32_t and uint64_t, from <stdint.h>, are the deliberate opposite: they guarantee an exact bit width on every platform, no exceptions. Reach for these instead of NSInteger/NSUInteger whenever code depends on an exact size -- serialized file formats, network protocols, cryptographic values, or anywhere a mismatched width between two platforms would silently corrupt data rather than just waste or save a few bytes.

• BOOL: YES/NO, historically a signed char typedef -- avoid comparing it directly against arbitrary non-0/1 integers. • NSInteger / NSUInteger: platform-dependent width -- long/unsigned long on 64-bit, int/unsigned int on 32-bit. • CGFloat: platform-dependent width -- float on 32-bit, double on 64-bit. • int32_t, uint64_t, and the rest of <stdint.h>: guaranteed exact width on every platform -- use these when exact size matters.

__kindof: Expressing a Family of Related Types

__kindof sits in between the precision of a concrete class type and the total genericity of id. A type like __kindof UIView * tells the compiler 'this is some subclass of UIView -- I don't know exactly which one until runtime, but it's definitely somewhere in UIView's family.' That's different from plain UIView *, which the compiler treats as exactly UIView (or an implicitly-upcast subclass) for checking purposes, and different again from id, which abandons class-based checking altogether.

The practical effect shows up at call sites: a method declared to return __kindof UIView * lets the caller send it any message that any UIView subclass might understand without an explicit cast, and the compiler will still catch a message that no UIView subclass could possibly understand. This is exactly the situation collection-returning methods use it for -- NSArray's firstObject-style APIs in class-constrained collections, or factory methods, like the example below, that hand back some unspecified member of a class family.

• __kindof ClassName *: 'some subclass of ClassName, exact identity unknown until runtime.' • Stricter than id (still checked against the ClassName family), looser than a concrete class type (allows any-subclass messages without a cast). • Commonly used on factory methods and on typed collections whose exact element subclass varies.

Object Pointers vs. C Pointers, void *, and Pointer-to-Pointer Types

An Objective-C object pointer -- NSString *, UIView *, and so on -- is not just 'a pointer' in the plain C sense: it's a pointer whose target is managed by Objective-C's memory management machinery (manual reference counting or ARC, as covered in the earlier Manual Reference Counting and ARC lessons). A plain C pointer, like int * pointing at a stack integer, points at raw memory with none of that management attached; nothing retains it, nothing releases it, and the language has no concept of its 'class' at all.

void * is the most generic pointer of all -- an untyped, opaque pointer that can point at literally anything, with the compiler performing no type checking whatsoever on what's read from or written through it. It's the type Objective-C code reaches for at the boundary with plain C APIs (buffers from malloc, C callback context parameters) precisely because it makes no claim about what's on the other end. It is easy to confuse with id because both feel 'generic,' but they are fundamentally different: id is still a real, memory-managed Objective-C object pointer that the runtime and compiler both understand as an object; void * is not an object pointer at all and carries none of that machinery.

A pointer-to-pointer type, like NSError or the NSString nameOut pattern, is a pointer whose target is itself a pointer -- it exists specifically to let a function or method write a new pointer value back to the caller through what's usually called an 'out parameter.' This pattern, and the ownership rules ARC attaches to it, is important enough to deserve its own full lesson (Ownership Qualifiers and Out Parameters), but the type itself belongs squarely in this inventory: it's simply one more pointer type, just one level deeper than usual.

• Object pointer (NSString *, UIView *, ...): points at a memory-managed Objective-C object -- retain/release or ARC applies. • Plain C pointer (int *, struct Foo *): points at raw memory with no Objective-C memory management attached. • void *: untyped, opaque pointer to anything -- no type checking at all, distinct from id despite both looking 'generic'. • Pointer-to-pointer (NSError **, id *): a pointer to a pointer, used to write a new pointer value back through an out parameter.

Ownership-Qualified Pointers: A Preview

Under ARC, every Objective-C object pointer variable also carries an ownership qualifier -- __strong, __weak, __unsafe_unretained, or __autoreleasing -- that tells the compiler how to manage the object it points to. __strong is the default and keeps the referenced object alive for as long as the variable holds it; __weak refers to an object without keeping it alive and automatically becomes nil if that object is deallocated elsewhere. These qualifiers are genuinely part of a variable's full type, not just a comment, which is why they can appear explicitly in a declaration.

This lesson only needs to establish that ownership qualifiers are part of the type system -- a full treatment of all four qualifiers, how they interact with parameters and out parameters like NSError **, and the compiler errors that result from mismatching them, is the entire subject of the next lesson, Ownership Qualifiers and Out Parameters.

• __strong (default): keeps the referenced object alive for the variable's lifetime. • __weak: refers to an object without keeping it alive; automatically becomes nil if the object is deallocated. • Full coverage, including __unsafe_unretained, __autoreleasing, and out-parameter interactions, is in the Ownership Qualifiers and Out Parameters lesson.

Function Pointers and Block Pointers

A function pointer is a C-level variable that holds the address of an actual function, with a type describing that function's return type and parameter types -- int (*mathOperation)(int, int) declares a variable that can hold the address of any C function taking two ints and returning an int. Function pointers predate Objective-C entirely; they're plain C, and Objective-C code uses them wherever it needs to hand a callback to a C API that has no concept of blocks or objects.

A block pointer looks superficially similar -- int (^blockOperation)(int, int) -- but the caret in place of the asterisk marks something conceptually different: a block is an Objective-C/C-extension construct that, unlike a bare function pointer, can capture variables from the surrounding scope and carry them along. Blocks are themselves managed like objects under ARC (they can be copied, retained, and released), whereas a function pointer is just an address with no captured state and no memory management of its own.

• Function pointer: int (*name)(int, int) -- holds a C function's address, no captured state, no ARC management. • Block pointer: int (^name)(int, int) -- can capture surrounding variables, is itself ARC-managed like an object. • Both describe 'a piece of callable code,' but only blocks close over their environment and participate in reference counting.

Nullability: Annotating Which Pointers May Legitimately Be nil

The Nullability lesson covers nullable, nonnull, and NS_ASSUME_NONNULL_BEGIN/END in full detail; the point worth restating here is that nullability annotations are part of the type system too -- they change what the compiler is willing to warn you about for a given pointer type, the same way __kindof changes what messages the compiler is willing to let through. A parameter or return value marked nullable tells the compiler that nil is an expected, legitimate value there, while one marked nonnull (the default inside an NS_ASSUME_NONNULL_BEGIN block) tells it that passing or returning nil is a programmer error worth flagging.

Nullability interacts directly with the pointer-to-pointer types covered above: an out parameter's nullability can be specified independently for the pointer itself and for the value it points to, written as something like NSError * _Nullable * _Nullable error, meaning both the NSError ** itself and the NSError * it eventually points to are allowed to be nil.

• nullable / nonnull are part of a pointer's full type, exactly like __kindof or an ownership qualifier. • nonnull is the default inside NS_ASSUME_NONNULL_BEGIN / NS_ASSUME_NONNULL_END, as the Nullability lesson covers. • Pointer-to-pointer nullability can be specified at both levels independently, e.g. NSError * _Nullable * _Nullable.

Type Casting and Safe Casting

A cast like (UIButton *)something changes what the compiler believes a pointer's type is -- nothing more. It performs no runtime check at all; if something doesn't actually point to a UIButton at runtime, the cast still compiles and still 'succeeds' at the language level, and the program only discovers the mismatch later, typically by crashing the first time it sends something a message that only UIButton understands.

Safe casting means verifying the object's actual runtime type before trusting a cast, almost always with isKindOfClass: (matches the class or any subclass) or isMemberOfClass: (matches exactly that class, no subclasses). This is the direct, practical payoff of the compile-time-type-vs-runtime-type distinction this lesson opened with: a cast only ever changes the compile-time type; isKindOfClass: is what actually consults the runtime type before your code acts on the assumption the cast encodes.

• A cast changes only the compile-time type the compiler reasons about -- it performs no runtime verification at all. • isKindOfClass: checks the actual runtime class (or any of its subclasses) before you trust a cast. • isMemberOfClass: checks for an exact class match, with no subclasses accepted. • Safe casting is the practical technique for managing the compile-time/runtime type gap this lesson is built around.

Code Examples

__kindof UIView * tells the compiler 'some subclass of UIView, exact identity unknown until runtime' -- stricter than id (still checked against the UIView family) but looser than a concrete UIView * (which would reject setTitle:forState: without a cast). This is the exact example from the type-system diagram: the compile-time type is __kindof UIView *, but the runtime object is an actual UIButton.

• (__kindof UIView *)makeView;

counterStartingAt: returns instancetype, so calling it on Counter (or any subclass) gives back precisely that type with no cast needed -- returning id instead would discard that precision. incrementSelector is a SEL: just a name. incrementImp is an IMP: the actual function pointer method lookup resolves that name to for Counter's class, matching the SEL/IMP distinction introduced in the Selectors and performSelector lessons.

IMP incrementImp = [counter methodForSelector:incrementSelector];

NSInteger, NSUInteger, and CGFloat are platform-dependent typedefs -- they resolve to a wider or narrower C type depending on whether the target is 32-bit or 64-bit, which is why casting them to long/unsigned long before logging with %ld/%lu avoids format-specifier mismatches across platforms. int32_t and uint64_t, from <stdint.h>, guarantee the same exact width everywhere, which is why fixed-width types are the right choice whenever exact size matters, such as serialized data.

int32_t fixedSmall = 100;

Each of these is a distinct kind of pointer with distinct rules: name is ARC-managed because it's an object pointer; cPointer is raw memory with no ARC involvement at all; rawBuffer is opaque and untyped; replaceName's parameter is a pointer-to-pointer used to write a value back to the caller; mathOperation holds a bare function address with no captured state; blockOperation holds a block, which -- unlike the function pointer -- closes over sum from the surrounding scope.

void replaceName(NSString **nameOut) { *nameOut = @"Grace"; }

Inside NS_ASSUME_NONNULL_BEGIN/END, every unannotated pointer type -- like userID and name here -- is treated as nonnull by default, exactly as the Nullability lesson describes. nameForUserID: is explicitly marked nullable because returning nil for an unknown user is expected. The error parameter shows nullability specified at both levels of a pointer-to-pointer: the NSError ** itself may be nil (the caller might not want an error back at all), and the NSError * it eventually points to may also be nil (no error occurred).

error:(NSError * _Nullable * _Nullable)error The unsafe cast only changes something's compile-time type -- it performs no verification against something's actual runtime type, so if the object underneath isn't really a UIButton, the mismatch surfaces later as a crash rather than immediately as a caught error. isKindOfClass: is what actually inspects the runtime type before the code commits to treating something as a UIButton, which is the whole point of distinguishing compile-time type from runtime type in the first place.

if ([something isKindOfClass:[UIButton class]]) {

@interface ViewFactory : NSObject
- (__kindof UIView *)makeView;
@end

@implementation ViewFactory
- (__kindof UIView *)makeView {
    return [[UIButton alloc] init];
}
@end

ViewFactory *factory = [[ViewFactory alloc] init];
__kindof UIView *view = [factory makeView];

// Because makeView is declared __kindof UIView *, the compiler allows
// UIButton-specific messages on the result without an explicit cast:
[view setTitle:@"Tap Me" forState:UIControlStateNormal];
@interface Counter : NSObject
@property (nonatomic) NSInteger count;
+ (instancetype)counterStartingAt:(NSInteger)value;
- (void)increment;
@end

@implementation Counter
+ (instancetype)counterStartingAt:(NSInteger)value {
    Counter *c = [[self alloc] init];
    c.count = value;
    return c;
}
- (void)increment {
    self.count += 1;
}
@end

Counter *counter = [Counter counterStartingAt:10];

// SEL: a runtime-registered identifier for the method name "increment" --
// carries no code of its own.
SEL incrementSelector = @selector(increment);

// IMP: the raw C function pointer to the actual code the runtime would
// jump to for -increment on Counter's class.
IMP incrementImp = [counter methodForSelector:incrementSelector];
void (*typedIncrement)(id, SEL) = (void (*)(id, SEL))incrementImp;
typedIncrement(counter, incrementSelector);
BOOL isReady = YES;
NSInteger score = -42;
NSUInteger itemCount = 7;
CGFloat opacity = 0.85;

int32_t fixedSmall = 100;
uint64_t fixedLarge = 18446744073709551615ULL;

NSLog(@"score=%ld count=%lu opacity=%f fixed=%d large=%llu",
      (long)score, (unsigned long)itemCount, opacity,
      fixedSmall, fixedLarge);
// Object pointer -- points to an Objective-C object, memory-managed by ARC
NSString *name = @"Ada";

// Plain C pointer -- points to raw memory, no Objective-C memory management
int localValue = 5;
int *cPointer = &localValue;

// void * -- untyped, opaque pointer; the compiler checks nothing about it
void *rawBuffer = malloc(64);
free(rawBuffer);

// Pointer-to-pointer -- lets a function write a new pointer value back
// through an "out parameter"
void replaceName(NSString **nameOut) {
    *nameOut = @"Grace";
}
NSString *current = @"Ada";
replaceName(&current);   // current is now @"Grace"

// Function pointer -- holds the address of a plain C function
int add(int a, int b) { return a + b; }
int (*mathOperation)(int, int) = add;
int sum = mathOperation(2, 3);

// Block pointer -- holds a reference to an Objective-C block, which can
// capture surrounding variables (sum, here) unlike a bare function pointer
int (^blockOperation)(int, int) = ^(int a, int b) {
    return a + b + sum;
};
int blockSum = blockOperation(4, 5);
NS_ASSUME_NONNULL_BEGIN

@interface UserStore : NSObject
- (nullable NSString *)nameForUserID:(NSString *)userID;
- (BOOL)saveUser:(NSString *)name
           error:(NSError * _Nullable * _Nullable)error;
@end

NS_ASSUME_NONNULL_END
id something = [factory makeView];

// Unsafe cast: compiles fine, but crashes at runtime if 'something'
// doesn't actually respond to allTargets or isn't really a UIButton
UIButton *unsafeButton = (UIButton *)something;

// Safe cast: verify the runtime type before trusting the cast
UIButton *safeButton = nil;
if ([something isKindOfClass:[UIButton class]]) {
    safeButton = (UIButton *)something;
} else {
    NSLog(@"Expected a UIButton but got a %@", NSStringFromClass([something class]));
}

From Source Code to Actual Object

__kindof expresses a family of related object types along this chain -- it affects compile-time type checking without pretending to know the actual class until runtime.

UIButton *button = (UIButton *)someObject; [button setTitle:@"Go" forState:UIControlStateNormal];

if ([someObject isKindOfClass:[UIButton class]]) { UIButton *button = (UIButton *)someObject; [button setTitle:@"Go" forState:UIControlStateNormal]; }

A cast alone never checks the object's actual runtime class -- it only changes what type the compiler treats the pointer as. Sending a message that the real object doesn't actually respond to crashes at runtime; isKindOfClass: is what verifies the runtime type before the cast is trusted.

• (id)counterStartingAt:(NSInteger)value { return [[self alloc] init]; } - (instancetype)counterStartingAt:(NSInteger)value { return [[self alloc] init]; }

Declaring a factory or init method to return id compiles, but it throws away type information the compiler could otherwise track for you -- callers on a subclass get back id, not the subclass type, and lose compile-time checking on the result. instancetype preserves the exact calling type with no runtime cost.

int32_t recordCount = someObject.count; // NSUInteger truncated into a 32-bit type

NSUInteger recordCount = someObject.count; // matches count's actual platform-dependent width // or, if an exact width is genuinely required for serialization: uint32_t serializedCount = (uint32_t)someObject.count;

NSInteger/NSUInteger change width depending on the platform (32-bit vs 64-bit); assigning one into a fixed-width type without an explicit, deliberate cast can silently truncate large values on 64-bit platforms. Match the declared type, or cast explicitly and intentionally when an exact width really is required.

void *buffer = objcStringVariable; // treating an object pointer as an untyped C pointer

NSString *objcStringVariable = @"data"; const char *cString = [objcStringVariable UTF8String]; void *buffer = (void *)cString; // explicit, intentional, and only for the C string bytes id and void * both look 'generic,' but id is a real, memory-managed Objective-C object pointer while void * is an untyped pointer to raw memory with no object semantics at all. Mixing them without an explicit, deliberate conversion breaks the assumptions both ARC and any C code on the other end are making.

What is the key difference between a method declared to return instancetype versus one declared to return id? A. There is no real difference; they compile to identical code B. instancetype tells the compiler the return type matches exactly the type the method was called on, preserving type checking; id discards that precision C. instancetype can only be used on class methods, while id can only be used on instance methods D. id is faster at runtime because it skips a type check that instancetype performs

B. instancetype tells the compiler the return type matches exactly the type the method was called on, preserving type checking; id discards that precision — instancetype is a compile-time-only annotation meaning 'the same type as whatever this method is called on.' A subclass calling an instancetype-returning factory or init method gets back that subclass's type with full compiler checking; id would give back a generic object pointer with no such precision.

What does a SEL value represent, and how does it differ from an IMP value? A. SEL and IMP are two names for the exact same underlying value B. SEL is a runtime-registered identifier for a method name, carrying no code; IMP is a raw function pointer to the actual implementation code for a given class C. SEL points to executable code; IMP is just a string of the method name D. IMP is only used for class methods, while SEL is only used for instance methods

B. SEL is a runtime-registered identifier for a method name, carrying no code; IMP is a raw function pointer to the actual implementation code for a given class — SEL is purely a name -- a token the runtime uses during method lookup. IMP is the actual C function pointer that method lookup resolves to for a specific class, which is what methodForSelector: hands back to you.

A method returns __kindof UIView *. What does that type tell the compiler, compared to returning plain UIView * or id? A. It's identical to returning id -- no type checking occurs at all B. It's identical to returning UIView * -- subclass-specific messages still require an explicit cast C. It tells the compiler the result is some subclass of UIView (exact class unknown until runtime), letting subclass-specific messages through without a cast, while still rejecting messages no UIView subclass could understand D. It forces the compiler to insert a runtime isKindOfClass: check automatically before any message is sent

C. It tells the compiler the result is some subclass of UIView (exact class unknown until runtime), letting subclass-specific messages through without a cast, while still rejecting messages no UIView subclass could understand — __kindof expresses membership in a class family: looser than a concrete UIView * (which would require a cast for subclass-specific messages) but stricter than id (which performs no class-based checking at all). It affects compile-time checking only -- it performs no automatic runtime verification.

Why does NSInteger require an explicit cast to (long) before being logged with %ld across both 32-bit and 64-bit platforms? A. NSInteger is always exactly 32 bits regardless of platform B. NSInteger is a platform-dependent typedef -- long on 64-bit platforms, int on 32-bit platforms -- so an explicit cast avoids format-specifier mismatches across platforms C. %ld only works with fixed-width types like int32_t, never with typedefs D. NSInteger cannot be logged with NSLog under any circumstances

B. NSInteger is a platform-dependent typedef -- long on 64-bit platforms, int on 32-bit platforms -- so an explicit cast avoids format-specifier mismatches across platforms — NSInteger resolves to different underlying C types depending on the platform's pointer width. Casting explicitly to (long) before using %ld guarantees the format specifier matches the actual value's type consistently, regardless of which platform the code runs on.

What is the safest way to determine whether an id-typed object can be treated as a specific subclass before sending it subclass-specific messages? A. Simply cast it to the subclass type -- the compiler will reject the cast at compile time if it's wrong B. Call isKindOfClass: with the target class before casting and acting on the assumption C. Check whether the variable's declared (compile-time) type matches the subclass D. Casts are always safe in Objective-C because id has no real type of its own

B. Call isKindOfClass: with the target class before casting and acting on the assumption — A cast changes only the compile-time type the compiler reasons about -- it performs no runtime verification. isKindOfClass: is what actually inspects the object's real runtime class (or its superclasses) before the code trusts a cast, which is the practical technique for bridging the compile-time-type/runtime-type gap.

What fundamentally distinguishes a block pointer (int (^)(int, int)) from a plain C function pointer (int (*)(int, int))? A. They are identical in every respect except syntax B. A block can capture and carry along variables from its surrounding scope and is itself ARC-managed; a bare function pointer holds only an address with no captured state and no reference counting C. Function pointers can only point to Objective-C methods, never plain C functions D. Blocks cannot be stored in variables, only function pointers can

B. A block can capture and carry along variables from its surrounding scope and is itself ARC-managed; a bare function pointer holds only an address with no captured state and no reference counting — A function pointer is just the address of a C function -- no captured state, no memory management. A block can close over surrounding variables and, under ARC, is itself a reference-counted object like any other Objective-C value.

Declare an @interface for a class ShapeFactory with a factory method makeShape that returns __kindof NSObject * (standing in for a family of shape subclasses), using instancetype for the return type is not appropriate here since the exact class varies -- explain in a comment why __kindof is the right choice over both instancetype and id. Then write calling code that safely casts the result to a concrete Circle class using isKindOfClass: before using a Circle-specific property.

@interface ShapeFactory: NSObject\n- (__kindof NSObject *)makeShape; // instancetype would be wrong: the actual\n // returned class varies at runtime and\n // isn't necessarily ShapeFactory itself;\n // id would be too loose, discarding the\n // fact that it's always some NSObject.\n@end\n\nid shape = [[ShapeFactory alloc] makeShape];\nif ([shape isKindOfClass:[Circle class]]) {\n Circle *circle = (Circle *)shape;\n NSLog(@"radius = %f", circle.radius);\n} instancetype means 'exactly the type this was called on' -- that's wrong for a factory that can return different, unrelated subclasses depending on internal logic. __kindof says 'some member of this family, exact identity unknown until runtime,' which is the accurate claim here.

Compile-Time Type
       ↓
Objective-C Pointer Type
       ↓
Runtime Object
       ↓
Actual Class