cv.cm

Coding · Programming languages

Objective-C tutorial for beginners

6 min read

Short answer

Objective-C is C with Smalltalk-style object-oriented messaging added on top. You call methods by sending messages in square brackets, [object doSomething:arg], define classes in @interface and @implementation blocks, and use Apple's Foundation framework for strings, arrays and dictionaries. It was the main language for macOS and iOS apps before Swift and is still used in many existing Apple code bases. On a Mac, compile with clang -fobjc-arc -framework Foundation hello.m.

What Objective-C is and who uses it

Objective-C was created by Brad Cox and Tom Love in the early 1980s, adopted by NeXT for its NeXTSTEP system, and inherited by Apple, where NeXTSTEP became the basis of macOS and iOS. It is a strict superset of C: any C code is valid Objective-C, and you can mix the two freely.

Today Apple recommends Swift for new apps, but Objective-C remains fully supported. It is found in older iOS and macOS apps, in many system frameworks and third-party libraries, and in projects that mix C or C++ with Apple APIs. Swift and Objective-C interoperate in the same project, so knowing both is useful for Apple platform developers.

Install and run Hello, world

On macOS, install Xcode or only the command line tools (xcode-select --install), which include the clang compiler and the Foundation framework. Source files use the .m extension; headers use .h.

// hello.m
#import <Foundation/Foundation.h>

int main(void) {
    @autoreleasepool {
        NSLog(@"Hello, world!");
    }
    return 0;
}
clang -fobjc-arc -framework Foundation hello.m -o hello
./hello
# 2026-10-31 10:00:00.000 hello[1234:5678] Hello, world!

NSLog writes to standard error and adds a timestamp and process name. @"..." creates an NSString object instead of a C string. -fobjc-arc turns on Automatic Reference Counting, the standard memory management. On Linux, the GNUstep project provides a compatible Foundation library, with different build commands.

Types, objects and messages

Because Objective-C is C, all C types work. Objects are always used through pointers (NSString *name). Calling a method is called sending a message; arguments are interleaved with the parts of the method name.

TypeExampleNotes
C numbersint, double, NSIntegerNSInteger is 64-bit on 64-bit platforms
BOOLBOOL ok = YES;YES / NO
NSString *@"hello"Immutable string object
NSNumber *@42, @3.14, @(x + 1)Boxes a number so collections can hold it
idid thing = @"x";Pointer to any object, no static type
nilnilThe null object pointer
NSString *name = @"Ana";
NSInteger age = 30;
NSString *line = [NSString stringWithFormat:@"%@ is %ld", name, (long)age];
NSLog(@"%@", line);                          // Ana is 30
NSLog(@"%lu", (unsigned long)[name length]); // 3
NSString *upper = [name uppercaseString];    // @"ANA"
BOOL same = [name isEqualToString:@"Ana"];   // YES
NSString *nothing = nil;
NSLog(@"%lu", (unsigned long)[nothing length]); // 0: messages to nil return 0

In format strings, %@ prints an object (it calls its description method). Sending a message to nil is allowed and returns nil or 0, which avoids many crashes but can also hide bugs.

Control flow

Control flow is exactly C: if, else, switch, for, while and do ... while. Objective-C adds fast enumeration with for ... in for collections.

NSInteger score = 85;
if (score >= 90) {
    NSLog(@"A");
} else if (score >= 80) {
    NSLog(@"B");
} else {
    NSLog(@"C");
}

for (int i = 1; i <= 3; i++) {
    NSLog(@"%d squared is %d", i, i * i);
}

for (NSString *fruit in @[@"apple", @"pear"]) {
    NSLog(@"%@", fruit);
}

Classes, properties and methods

A class has a public interface (usually in a .h file) and an implementation (in the .m file). Instance methods start with -, class methods with +. @property declares a property and generates its getter, setter and a backing variable named with an underscore.

@interface Person : NSObject
@property (nonatomic, copy) NSString *name;
@property (nonatomic) NSInteger age;
- (instancetype)initWithName:(NSString *)name age:(NSInteger)age;
- (NSString *)greeting;
@end

@implementation Person
- (instancetype)initWithName:(NSString *)name age:(NSInteger)age {
    self = [super init];
    if (self) {
        _name = [name copy];
        _age = age;
    }
    return self;
}

- (NSString *)greeting {
    return [NSString stringWithFormat:@"Hi, I am %@, %ld", self.name, (long)self.age];
}
@end

// Usage inside main:
Person *ana = [[Person alloc] initWithName:@"Ana" age:30];
NSLog(@"%@", [ana greeting]);   // Hi, I am Ana, 30
ana.age = 31;                   // dot syntax calls the setter

Objects are created in two steps: alloc reserves memory and an init... method initializes it. The method above is named initWithName:age: (the colons are part of the name). Protocols (@protocol) play the role of interfaces, and categories let you add methods to existing classes, even NSString.

Foundation collections and blocks

The Foundation framework provides immutable collections and mutable subclasses. Literals make them short to write. Collections hold only objects, so numbers are boxed as NSNumber, and nil cannot be stored (use [NSNull null] instead).

ClassLiteral or creationMutable version
NSArray@[@"Ana", @"Ben"]NSMutableArray
NSDictionary@{@"Ana": @30}NSMutableDictionary
NSSet[NSSet setWithArray:arr]NSMutableSet
NSString@"text"NSMutableString
NSArray<NSString *> *names = @[@"Ana", @"Ben"];
NSMutableArray<NSString *> *more = [names mutableCopy];
[more addObject:@"Cleo"];
NSDictionary<NSString *, NSNumber *> *ages = @{@"Ana": @30, @"Ben": @17};
NSLog(@"%lu names, Ana is %@", (unsigned long)more.count, ages[@"Ana"]);

NSInteger (^square)(NSInteger) = ^(NSInteger x) { return x * x; };
NSLog(@"%ld", (long)square(7));    // 49

NSArray<NSNumber *> *sorted = [@[@3, @1, @2]
    sortedArrayUsingComparator:^NSComparisonResult(NSNumber *a, NSNumber *b) {
        return [a compare:b];
    }];
NSLog(@"%@", [sorted componentsJoinedByString:@", "]);   // 1, 2, 3

Blocks, written with ^, are closures: anonymous functions that capture variables from the surrounding scope. Apple APIs use them for callbacks, sorting and asynchronous work with Grand Central Dispatch. The <NSString *> parts are lightweight generics that help the compiler and Swift; they are optional.

Memory management and error handling

With ARC, the compiler inserts reference-counting calls for you: an object is freed when no strong reference points to it. You only need to break reference cycles, usually by marking one side weak (for example, a delegate property).

Recoverable errors use the NSError pattern: a method returns nil or NO on failure and fills an NSError object passed by reference. Exceptions (@try, @catch, NSException) exist, but in Cocoa they signal programmer errors such as an index out of range, not normal failures.

NSError *error = nil;
NSString *text = [NSString stringWithContentsOfFile:@"/no/such/file"
                                           encoding:NSUTF8StringEncoding
                                              error:&error];
if (text == nil) {
    NSLog(@"Failed: %@", error.localizedDescription);
}

A small complete program: word frequencies

#import <Foundation/Foundation.h>

int main(void) {
    @autoreleasepool {
        NSString *text = @"the cat and the hat and the bat";
        NSMutableDictionary<NSString *, NSNumber *> *counts = [NSMutableDictionary dictionary];
        for (NSString *word in [text componentsSeparatedByString:@" "]) {
            counts[word] = @(counts[word].integerValue + 1);
        }
        NSArray<NSString *> *sorted = [counts keysSortedByValueUsingComparator:
            ^NSComparisonResult(NSNumber *a, NSNumber *b) { return [b compare:a]; }];
        for (NSString *word in [sorted subarrayWithRange:NSMakeRange(0, 2)]) {
            printf("%s %ld\n", word.UTF8String, (long)counts[word].integerValue);
        }
    }
    return 0;
}
// the 3
// and 2

For a new word, counts[word] is nil, and nil.integerValue returns 0, so the first count becomes 1. The comparator reverses the order to sort by count from high to low, and printf prints without the NSLog prefix.

Where to go next

  • Apple's "Programming with Objective-C" guide in the documentation archive for the language basics.
  • The Foundation framework reference in Apple's developer documentation.
  • The Clang documentation on Automatic Reference Counting for the exact memory rules.
  • Learn how Objective-C and Swift work together if you maintain a mixed code base.

FAQ

Is Objective-C still used?

Yes, mainly in existing iOS and macOS apps and libraries. Apple recommends Swift for new code, but Objective-C is still supported by Xcode and the Apple SDKs.

Should I learn Swift or Objective-C first?

For new Apple development, learn Swift first. Learn Objective-C when you need to read or maintain older code, use C or C++ libraries closely, or understand how Apple frameworks work underneath.

What do the square brackets mean in Objective-C?

They send a message: [receiver message] calls the method message on the object receiver. Arguments follow colons, as in [dict setObject:value forKey:key].

Does Objective-C have garbage collection?

Modern Objective-C uses Automatic Reference Counting (ARC), where the compiler adds retain and release calls. Apple's older garbage collector for macOS was deprecated and is no longer available.

All coding tutorials