100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
Programming

Methods and Message Passing

Explore how Objective-C methods work as messages sent to objects at runtime, including selectors, method signatures, and dynamic dispatch.

Control Flow & MethodsIntermediate10 min readJul 10, 2026
Analogies

Messages, not function calls

Unlike C++ or Java, where calling a method is (mostly) resolved to a fixed function address at compile time, Objective-C method calls are messages sent to a receiver via the Objective-C runtime's objc_msgSend function. Writing [receiver doSomething:argument] doesn't directly jump to a function; it compiles down to something like objc_msgSend(receiver, @selector(doSomething:), argument), which looks up the correct implementation at runtime based on the receiver's actual class.

🏏

Cricket analogy: Sending a message to a receiver is like a captain shouting a fielding instruction to 'point' rather than directly grabbing that fielder's legs — the instruction is dispatched, and it's up to that specific fielder to interpret and execute it.

This dynamic dispatch is what makes it valid to send a message to nil in Objective-C without crashing: [nilObject doSomething] simply returns nil (or zero/NULL for scalar return types), because objc_msgSend checks the receiver first and returns immediately if it's nil, rather than dereferencing a null pointer as C++ would. This behavior is frequently exploited to avoid verbose nil checks, though it can also mask bugs where a value silently becomes nil earlier than expected.

🏏

Cricket analogy: Sending an instruction to an empty slip cordon (no fielder there) simply results in nothing happening rather than the game crashing — the same graceful no-op as messaging nil in Objective-C.

Selectors and method signatures

A selector, represented by the SEL type, is essentially the name of a method — for example @selector(setName:age:) — used by the runtime to look up the actual implementation (an IMP, a function pointer) at message-send time. Selectors are interned strings, meaning identical selector names always compare equal, which lets you check whether an object responds to a message with respondsToSelector: before invoking it dynamically via performSelector: or NSInvocation for more complex argument lists.

🏏

Cricket analogy: A selector is like a fielding position name such as 'gully' — the name itself is fixed and interned across every match, but which actual fielder stands there (the implementation) can change from game to game.

Dot notation vs bracket notation

Dot notation, such as person.name = @"Ada";, is pure syntactic sugar over message sends and is generally reserved for property access (getters/setters generated by @property), compiling to [person setName:@"Ada"]; behind the scenes. Bracket notation, [person setName:@"Ada"], is required for any method that isn't a property accessor, and many teams enforce a style rule of using dot notation only for properties to keep the distinction between 'accessing state' and 'sending a behavioral message' visually clear.

🏏

Cricket analogy: Checking .score on a scoreboard app is like glancing at a fixed display panel for a property, while calling [scoreboard incrementRuns:4] is like actively signaling the scorer to perform an action — two different kinds of interaction with the same object.

objectivec
@interface Person : NSObject
@property (nonatomic, copy) NSString *name;
- (void)greetWithGreeting:(NSString *)greeting;
@end

@implementation Person
- (void)greetWithGreeting:(NSString *)greeting {
    NSLog(@"%@, %@!", greeting, self.name);
}
@end

// Usage
Person *ada = [[Person alloc] init];
ada.name = @"Ada";                       // dot notation -> setName:
[ada greetWithGreeting:@"Hello"];         // bracket notation, message send

SEL greetSelector = @selector(greetWithGreeting:);
if ([ada respondsToSelector:greetSelector]) {
    [ada performSelector:greetSelector withObject:@"Hi"];
}

// Message to nil is safe and returns nil
Person *missing = nil;
NSString *result = [missing name]; // result is nil, no crash

performSelector: and its variants don't let ARC verify memory-management semantics at compile time, since the compiler can't know what the selector returns; this can lead to retain/release mismatches. Modern Objective-C code often prefers blocks or direct message sends where possible, reserving performSelector: for genuinely dynamic cases.

  • Objective-C method calls compile down to objc_msgSend, a dynamic dispatch mechanism, not direct function calls.
  • Sending any message to nil safely returns nil/0/NULL instead of crashing.
  • A SEL (selector) is an interned name for a method, resolved to an IMP (implementation) at runtime.
  • respondsToSelector: lets you check dynamically whether an object implements a given method.
  • Dot notation is sugar for property accessor message sends (getters/setters).
  • Bracket notation is required for any non-property method call.
  • performSelector: bypasses some ARC compile-time safety checks and should be used sparingly.

Practice what you learned

Was this page helpful?

Topics covered

#Programming#ObjectiveCStudyNotes#MethodsAndMessagePassing#Methods#Message#Passing#Messages#Functions#StudyNotes#SkillVeris