Skip to content
Lesson 6 of 6

Step 1 of 5 · Reading · ~3 min

Learn

Collections, Control Flow, Selectors

In Objective-C, methods are selectors — symbolic representations of method names. They let you store, pass, and dynamically dispatch method calls.

Selector basics

SEL action = @selector(uppercaseString);

SEL is just a name. You can apply it to any object:

NSString *s = @"hello";
if ([s respondsToSelector:@selector(uppercaseString)]) {
    NSString *upper = [s performSelector:@selector(uppercaseString)];
    printf("%s\n", [upper UTF8String]);
}
  • @selector(name:) — get a selector for a method (note the colon for methods with args)
  • respondsToSelector: — check if object has this method
  • performSelector: — call by selector

Selectors with arguments

[s performSelector:@selector(stringByAppendingString:) withObject:@" world"]
// "hello world"

performSelector:withObject:withObject: for two args. Beyond two: use NSInvocation (more verbose).

Storing selectors

Dispatch tables become possible:

NSDictionary *handlers = @{
    @"uppercase": [NSValue valueWithPointer:@selector(uppercaseString)],
    @"lowercase": [NSValue valueWithPointer:@selector(lowercaseString)],
    @"reverse":   [NSValue valueWithPointer:@selector(stringByReversingString)]
};

Target-action pattern

Classic Cocoa:

[button addTarget:self action:@selector(buttonClicked:) forControlEvents:UIControlEventTouchUpInside];

// On click, [self buttonClicked:button] is called.

This decoupling is fundamental to Cocoa's MVC.

Blocks (modern alternative)

Objective-C 2.0 (with C extensions) added blocks — lambda expressions. Most modern Cocoa code uses blocks instead of selectors:

int (^add)(int, int) = ^int(int a, int b) {
    return a + b;
};

int result = add(3, 4);    // 7

The ^ is the block syntax. int (^add)(int, int) is a block-pointer type: "block taking two ints, returning int".

Blocks capture variables:

int multiplier = 5;
int (^times)(int) = ^int(int n) { return n * multiplier; };
times(3);    // 15

Blocks for collection iteration:

NSArray *nums = @[@1, @2, @3];
[nums enumerateObjectsUsingBlock:^(NSNumber *n, NSUInteger idx, BOOL *stop) {
    printf("%lu: %d\n", idx, [n intValue]);
}];

Sorting with a block:

NSArray *sorted = [nums sortedArrayUsingComparator:^NSComparisonResult(NSNumber *a, NSNumber *b) {
    return [b compare:a];   // descending
}];

This judge cannot compile blocks. Its Objective-C toolchain is built without -fblocks, so every ^ literal above fails with blocks support disabled - compile with -fblocks. Read the block sections for the concept — you will meet blocks constantly in real Cocoa and they are the direct ancestor of Swift closures — but reach for selectors when you write code here.

Selectors vs blocks

  • Selectors — older, used in target-action, NSTimer, KVO
  • Blocks — newer, much more common in modern code, can capture variables

Swift inherited blocks as closureslet f: (Int) -> Int = { x in x * 2 }. They're the same machinery underneath.

Why this matters

Objective-C's dynamism (selectors, message forwarding, dynamic typing) is what makes:

  • Auto-generated UIs (Interface Builder)
  • KVO (Key-Value Observing) for reactive code
  • Dependency injection without DI frameworks
  • Mocking for tests

Understanding selectors is essential for reading legacy Cocoa code and writing extensions to NSObject behavior.

Your exercise

Give a Doubler class one method, take that method as a SEL with @selector(...), and invoke it through performSelector:withObject: rather than by writing the message out directly. That indirection is the whole point: the call site never names the method.

The mistake the grader catches: performSelector:withObject: traffics in id, not in primitives. Pass a bare int and the runtime reads it as a pointer. Box the input with [NSNumber numberWithInt:n] on the way in and unbox with intValue on the way out.

Discussion

Ask a question, share an insight, or help someone who’s stuck.

Sign in to post a comment or reply.

Loading…