Anatomy of an Objective-C file
Objective-C is a superset of C that adds Smalltalk-style messaging, used extensively for Apple's macOS and iOS development before Swift.
File extensions: .m, .h
Every part of the example below is labelled and explained. This page is one of 55 annotated tours on AnatomyOf, a free, open-source project by LunarWerx Studios.
What is inside an Objective-C file
Comment
Single-line (//) or block (/* ... */), ignored by the compiler.
Because Objective-C is a strict superset of C, it inherits C's two comment forms verbatim: // runs to the end of the line, and /* ... */ spans multiple lines but does not nest. Both are stripped before compilation and have no effect on the compiled binary whatsoever. Header files (.h) lean on comments especially heavily, since they are the public contract of a class, the declarations a .m implementation file promises to fulfill. A well-commented header often serves as the only documentation a consumer of a framework ever reads.
Import directive
Includes header files and automatically prevents multiple inclusions.
#import <Foundation/Foundation.h> pulls in Foundation's declarations: NSString, NSObject, NSLog, and the rest of the base classes almost every Objective-C file depends on. Angle brackets search framework/system include paths; quotes ("MyHeader.h") search the local project directory first. #import is Objective-C's own preprocessor extension over plain C's #include: it tracks which files it has already pulled in and silently skips repeats, so headers no longer need hand-written #ifndef include guards. It is otherwise textual substitution performed by the same preprocessor pass C uses.
Interface declaration
Defines a class's public API with @interface, inheriting from a superclass.
@interface Person : NSObject { ... } opens a class declaration named Person, inheriting from NSObject, the root class most Objective-C objects ultimately descend from. Everything between @interface and the matching @end (ivars, property declarations, and method signatures) makes up the class's public interface, conventionally placed in a .h header so other files can #import it. Objective-C supports single class inheritance: a class has exactly one superclass. Shared contracts across unrelated hierarchies are expressed with protocols, while categories can add methods to an existing class withou
Instance variable
Private data stored in an object, often prefixed with an underscore.
NSInteger _age; declared inside the @interface braces is an instance variable (ivar). Raw storage that belongs to every instance of the class, invisible outside it by default. The leading underscore is a long-standing convention signaling "backing storage, do not touch directly," reserving the un-prefixed name for a property's public accessor. Modern Objective-C rarely hand-declares ivars for properties, since @property auto-synthesizes a matching underscored ivar for you. Explicit ivars still show up for state that should never be exposed through a property at all.
Property declaration
Declares accessor methods and ownership semantics; modern compilers normally synthesize their implementation.
@property (nonatomic, strong) NSString *name; declares a property and asks the compiler to synthesize -name and -setName: accessor methods plus a backing _name ivar, all without writing a line of implementation. The parenthesized attributes configure the generated accessors: nonatomic skips the overhead of thread-safe locking (the default, atomic, adds it), and strong tells Automatic Reference Counting to retain the object for as long as this property holds it. Other common attributes include weak (a non-owning reference that zeroes itself out when the object is deallocated, avoiding retain cy
Method declaration
Defines a method's signature; - for instance methods, + for class methods.
A leading - marks an instance method (- (void)sayHello; operates on a specific object), while a leading + marks a class method (+ (Person *)personWithName:(NSString *)name age:(NSInteger)age; operates on the class itself, commonly used for factory constructors). The parenthesized type before the selector fragment is the return type, and each labeled colon segment (name:, age:) is part of the method's full selector name. Unlike C++ or Java, Objective-C selectors are not overloaded by parameter type alone. personWithName:age: is one indivisible name, and every keyword before a colon is meant to
Implementation block
Contains the actual code for a class's methods, opened with @implementation.
@implementation Person ... @end supplies the bodies for the methods declared in the matching @interface. By convention this block lives in the .m ("message" or "methods") file, kept separate from the .h header so consumers of a class see only its public shape, not its internals. A class's interface and implementation can even disagree slightly: a "class extension" (@interface Person () @end in the .m file) can declare additional private properties or methods visible only within the implementation file, a common pattern for hiding internal state from the public header.
Method definition
The actual implementation of a declared method.
Inside @implementation, each method from the interface gets a matching body, such as - (instancetype)initWithName:(NSString *)name age:(NSInteger)age { ... }. instancetype is a special return type meaning "whatever class this was actually invoked on," which lets initializers and factory methods return the correct type even through subclassing, something a literal class name in the signature could not do safely. Initializers follow a strict idiom: call a superclass initializer, guard the result, assign ivars, then return self. Any initializer that skips the if (self) guard risks configuring an
Self keyword
Refers to the current instance of the class.
self inside an instance method refers to the object the message was sent to, analogous to this in C++ or Java. Inside a class method, self instead refers to the class object itself, which is why [self alloc] inside a + method allocates an instance of whatever class actually received the call, correctly supporting subclasses without being hard-coded. self.name reads through the synthesized accessor (calling -name), which is a meaningfully different operation than reaching directly for the ivar _name: the accessor path respects strong/copy/weak memory semantics and any custom logic layered into
Logging
Prints formatted output to the console with NSLog.
NSLog(@"Hello, my name is %@ and I am %ld years old.", self.name, (long)_age); writes a timestamped, process-tagged line to the console, formatted with printf-style conversion specifiers. %@ is Objective-C's own addition to the format-string vocabulary: it calls -description on any Objective-C object and substitutes the resulting string, which is why NSLog can print an NSString, NSArray, or custom object without a manual %s and C-string conversion. NSInteger is a platform-dependent typedef (32-bit or 64-bit depending on the architecture), so NSLog has no single matching format specifier for it
Main function & autorelease pool
The entry point, using @autoreleasepool to manage memory for temporary objects.
Like plain C, execution begins at int main(int argc, const char *argv[]). Wrapping the body in @autoreleasepool { ... } establishes a pool that drains at the closing brace, deallocating any object that was sent -autorelease (directly, or indirectly through convenience constructors) inside that scope. Under Automatic Reference Counting this pattern matters less for basic memory safety than it did under manual retain/release, but it still bounds the lifetime of temporary objects and keeps peak memory usage down in loops that create many short-lived objects, such as processing images or parsing l
Method call
Sends a message to an object using bracket syntax: [receiver message].
[bob sayHello]; is a message send: the runtime looks up sayHello in bob's class (and its superclasses) and invokes whatever it finds, resolved dynamically at runtime rather than bound at compile time the way a C function call is. [Person personWithName:@"Bob" age:30]; shows the same syntax with a multi-part selector, each argument slotting after its matching colon-labeled keyword. Because dispatch happens at runtime through the Objective-C runtime library, sending a message to nil is not a crash (it silently returns nil/0/NO) which is a deliberate design choice distinct from most C-family lang
Official Objective-C site · All languages on AnatomyOf
The interactive tour needs JavaScript. Enable it to hover a callout and trace it into the code.