Class Methods: The + Prefix, the Class Object, and Factory Methods

Explain the difference between a class method (+) and an instance method (-), understand that an Objective-C class is itself a runtime object that class methods are sent to, and write and use factory methods that create and return a fully-initialized instance in a single call.

The 'Objective-C Class Anatomy' lesson introduced Person with instance methods like -initWithName:age: and -description — methods you send to a specific Person instance, like Alice or Bob. But you've also been calling methods like [NSString stringWithFormat:...] and [[Person alloc] init] since nearly the start of this course, and those look different: they're sent directly to the class name, NSString or Person, not to any particular instance. These are class methods, marked with a + instead of a -, and they behave in a genuinely different way — not just cosmetically. This lesson explains what that + actually means, what it means for a class to be an object in its own right, and the extremely common convention of using class methods as factory methods that build and hand you back a ready-to-use instance in one call.

The + Prefix: A Method Sent to the Class, Not an Instance

Every method declaration in Objective-C begins with either - or +. A - (minus) prefix declares an instance method — a method you can only call by sending a message to a specific, already-existing instance, like [person description] where person is a particular Person object with its own name and age. A + (plus) prefix declares a class method — a method you call by sending a message to the class itself, using the class's name in place of an instance variable: [Person personWithName:@"Alice" age:30]. Notice there is no instance anywhere in that call; Person the class name is standing in the receiver position of the message, exactly where person stood in the instance-method example.

This distinction is declared, not inferred, and it must match on both sides. In the @interface, + (instancetype)personWithName:(NSString *)name age:(NSInteger)age; declares a class method, and in @implementation you write + (instancetype)personWithName:(NSString *)name age:(NSInteger)age { ... } with the same +. If you declare a method with + but accidentally implement it with - (or vice versa), the compiler treats them as two entirely unrelated methods — the declared class method has no implementation (a linker/runtime error when called), and the - version becomes an unrelated, undeclared instance method that produces a compiler warning wherever it's used.

A useful rule of thumb for deciding which prefix a method needs: if the method's job requires access to a particular object's own data — its ivars, its own current state — it must be an instance method (-), because that data only exists once an instance has been created. If the method's job is about the type as a whole, independent of any single object's data — creating a new instance, returning a shared/default value, reporting something true of every instance of the class — it's a natural candidate for a class method (+).

• - prefix: instance method, sent to a specific existing object (e.g. [person description]). • + prefix: class method, sent to the class itself, using the class name as the receiver (e.g. [Person personWithName:...]). • The +/- prefix must match exactly between the @interface declaration and the @implementation definition. • Rule of thumb: needs a specific instance's own data → instance method; concerns the type as a whole → class method.

The Class Object: A Class Is Itself an Object

It's easy to think of Person as merely a label — the name of a blueprint you use to stamp out real objects — but at runtime, Person is itself an object. When the Objective-C runtime loads your program, it creates one object per class, called the class object, typed as Class. This class object is what you're actually sending a message to whenever you write [Person alloc] or [Person personWithName:...] — the class name in source code is really a reference to this runtime object, resolved by the compiler and runtime, not a special piece of syntax that behaves unlike anything else.

This is why +alloc, the method that allocates memory for a new instance before -init sets it up, is itself a class method: [Person alloc] sends the alloc message to the Person class object, asking it to allocate a new, blank chunk of memory sized correctly for a Person instance and return it (typed, thanks to instancetype, as a Person pointer). Only after that memory exists does -init (an instance method) get sent to it, to fill in its ivars. That's the real reason [[Person alloc] init] is two separate messages chained together: alloc is asked of the class object, init is asked of the freshly allocated instance it hands back.

Every class object also has its own class — its metaclass — which is where class methods themselves are actually looked up during message dispatch; this is a genuinely deeper runtime detail that becomes clear once you study Objective-C's runtime and message-dispatch machinery directly, in the Runtime module later in this course. For now, the essential fact to hold onto is simply this: Person, NSString, NSArray — every class name you've been typing — refers to a real, live object your program can send messages to, and that object is exactly what receives every + method call.

• At runtime, every Objective-C class has a corresponding class object (type Class) that actually exists as an object. • [Person alloc], [Person personWithName:...] — the class name is a reference to this class object, the true message receiver. • +alloc is a class method sent to the class object; the instance it returns is what -init is then sent to. • Class objects have their own class (a metaclass), where class methods are looked up — covered fully in the Runtime module.

Declaring and Calling a Class Method

Declaring a class method looks exactly like declaring an instance method, with + in place of -, and it goes through the same two-part declaration-then-definition structure as every other Objective-C method. In the @interface block: + (instancetype)personWithName:(NSString *)name age:(NSInteger)age;. The return type instancetype here means 'an instance of whatever class this method is actually called on' — for a class method that creates and returns a new object, instancetype is almost always the right return type, for the same reasons it's the right choice for -init methods (covered in Class Anatomy): it plays correctly with subclassing, so a subclass calling this same class method gets back an instance of the subclass, not hard-coded as Person *.

In the @implementation block, the method body of a class method can still refer to self — but inside a class method, self refers to the class object itself (e.g. Person, the Class object), not to any instance, since there is no instance yet when a factory method like this one starts running. This is exactly how [self alloc] inside a class method correctly allocates an instance of whichever class the method was actually invoked on, rather than hard-coding [Person alloc] — a detail that matters once subclasses are involved, covered further in the Inheritance module.

Calling a class method from anywhere else in your code uses the same bracket message syntax as any other message send, with the class name as the receiver: Person *alice = [Person personWithName:@"Alice" age:30];. There is no special syntax to memorize beyond what you already know for sending any Objective-C message — the only thing that changed is what's sitting in the receiver position.

• Class method syntax: + (returnType)methodName:(paramType)param; — identical shape to instance methods, + instead of -. • instancetype is the conventional return type for a class method that creates and hands back a new instance. • Inside a class method's implementation, self refers to the class object itself, not to any instance. • [self alloc] inside a class method correctly allocates an instance of whatever class the method was actually called on (matters for subclasses).

Factory Methods: Building and Returning a Ready Instance in One Call

A factory method is a class method whose entire job is to create, fully configure, and return a ready-to-use instance in a single message send — hiding the separate alloc + init steps from the caller. You've been using this pattern from Apple's own frameworks all along: [NSString stringWithFormat:@"Score: %d", score] and [NSArray arrayWithObjects:@"a", @"b", nil] are both factory methods — neither one requires you to write alloc yourself; the method does that internally and hands you back a fully-formed object.

Writing your own factory method for Person is a thin, convenient wrapper around the designated initializer you already know from Class Anatomy: + (instancetype)personWithName:(NSString *)name age:(NSInteger)age { return [[self alloc] initWithName:name age:age]; }. Inside, it does exactly the two steps a caller would otherwise have written out by hand — alloc, then initWithName:age: — just packaged behind one message name, so calling code reads as Person *alice = [Person personWithName:@"Alice" age:30]; instead of the more verbose two-step Person *alice = [[Person alloc] initWithName:@"Alice" age:30];.

The convention for naming a factory method is to start the method name with a lowercase version of the class name, describing what it's built from — personWithName:age:, stringWithFormat:, arrayWithObjects: — which both signals at a glance 'this returns a new instance of this class' and matches the naming style Apple's own frameworks use pervasively, so your custom factory methods read naturally alongside built-in ones. Note, though, that a factory method is a convenience on top of alloc/init, not a replacement for it at the language level — the caller could always still write [[Person alloc] initWithName:name age:age] directly; the factory method just saves the caller a line and communicates clearer intent.

• A factory method is a class method that internally calls alloc + an initializer and returns the finished instance in one message send. • Familiar built-in factory methods: [NSArray arrayWithObjects:...], [NSString stringWithFormat:...], [NSDictionary dictionaryWithObjectsAndKeys:...]. • Custom example: + (instancetype)personWithName:(NSString *)name age:(NSInteger)age { return [[self alloc] initWithName:name age:age]; }. • Naming convention: start with the lowercase class name describing what's being built, e.g. personWithName:age:. • A factory method is a convenience wrapper, not a replacement — [[Person alloc] initWithName:age:] still works directly.

Class Methods Beyond Factories

Not every class method exists to create an instance. Class methods are the right tool anytime a piece of behavior or data genuinely belongs to the type as a whole rather than to any one object — a shared/default instance (+ (Person *)defaultGuest, always returning the same placeholder Person), a piece of information independent of any instance's own state (+ (NSInteger)maximumAge, reporting a constant that applies to every Person), or a utility computation that doesn't need any instance's ivars at all (a validation helper that checks whether a given name string would be acceptable, before you've even created an object to hold it).

A useful test when you're unsure whether something should be a class method or an instance method: try writing the method body and see whether it ever needs to read or write self's own ivars (like _name or _age on a particular Person). If it does, it needs an instance to operate on and must be an instance method. If the method's logic never touches per-instance data — it only uses its parameters, or returns something fixed — a class method is the better, more honest signal of what the method actually does.

It's also worth being clear about what class methods cannot do: because a class method is sent to the class object, not to any instance, it has no access to instance variables at all inside its body — there is no _name to read, because no particular Person's storage is in scope. Trying to reference an instance variable directly inside a class method's implementation is a compiler error, precisely because the compiler correctly recognizes that a class method never has an instance's storage available to it.

• Class methods are also appropriate for shared/default values, type-level constants, and utility logic independent of any instance. • Test: does the method body need to read/write a specific instance's ivars? Yes → instance method. No → candidate for a class method. • A class method has no access to instance variables in its body — there's no instance's storage in scope; the compiler rejects it as an error. • Class methods complement, not replace, instance methods — well-designed classes typically use both.

Code Examples

The + method is declared and implemented with the + prefix, matching on both sides. Inside its body, [self alloc] sends alloc to whichever class the method was actually called on (Person here), then chains the existing -initWithName:age: instance method onto the freshly allocated instance, and returns the fully-initialized result — the caller never has to write alloc or init directly.

+maximumAge never reads any instance's ivars — it just returns a fixed value true of the type as a whole, so it's correctly a class method, called on Person directly. -isOlderThan: needs self.age, which only exists once a specific Person (alice) exists, so it must be an instance method, sent to alice with bob passed as the argument.

+ (instancetype)personWithName:(NSString *)name age:(NSInteger)age; // ... - (instancetype)personWithName:(NSString *)name age:(NSInteger)age { return [[self alloc] initWithName:name age:age]; }

+ (instancetype)personWithName:(NSString *)name age:(NSInteger)age; // ... + (instancetype)personWithName:(NSString *)name age:(NSInteger)age { return [[self alloc] initWithName:name age:age]; }

The prefix in @implementation must match the prefix declared in @interface. Declaring a + method but implementing it with - produces two unrelated methods: the declared class method has no implementation (fails when called), and the accidental instance method is undeclared and unused.

+ (instancetype)personWithName:(NSString *)name age:(NSInteger)age { return [[Person alloc] initWithName:name age:age]; }

+ (instancetype)personWithName:(NSString *)name age:(NSInteger)age { return [[self alloc] initWithName:name age:age]; }

Hard-coding the class name (Person) instead of using self inside a class method works for Person itself, but breaks correctly-behaved subclassing: if a subclass inherits this factory method and calls it, [self alloc] allocates an instance of the subclass, while [Person alloc] would incorrectly always allocate a plain Person.

+ (NSString *)greeting { return [NSString stringWithFormat:@"Hi, %@", _name]; } - (NSString *)greeting { return [NSString stringWithFormat:@"Hi, %@", self.name]; }

A class method has no instance in scope, so there is no _name ivar available to read inside it — this is a compiler error. If a method's logic needs a specific instance's own data, it must be declared and implemented as an instance method (-), not a class method (+).

What does the + prefix on a method declaration mean? A. The method runs faster than a - method B. It's a class method, sent to the class object itself rather than to a specific instance C. It's a private method that can't be called from outside the class D. It's a method that always returns a numeric value

B. It's a class method, sent to the class object itself rather than to a specific instance — + declares a class method, called by sending a message to the class name (the class object) rather than to a particular instance, as opposed to - which declares an instance method sent to a specific object.

At runtime, what is Person when you write [Person alloc]? A. A keyword with no real existence, purely compiler syntax B. A reference to an actual object — the class object — that the alloc message is sent to C. A pointer to the first Person instance ever created D. A macro that expands to the word 'new'

B. A reference to an actual object — the class object — that the alloc message is sent to — Every Objective-C class has a corresponding class object at runtime (typed Class); class names in source code refer to this real object, which is exactly what class methods like +alloc are sent to.

What is a factory method? A. Any method that returns void B. A class method that internally performs alloc plus initialization and returns a fully-ready instance in one call C. A method that can only be called once per program D. A special syntax unrelated to normal message sending

B. A class method that internally performs alloc plus initialization and returns a fully-ready instance in one call — A factory method, like Person's +personWithName:age: or Foundation's +stringWithFormat:, wraps alloc and an initializer behind a single class-method call, saving the caller from writing both steps separately.

Inside the implementation of a class method, what does self refer to? A. The most recently created instance of the class B. The class object itself, since no instance exists yet when a class method like a factory method runs C. Nothing — self cannot be used inside a class method D. The superclass's instance

B. The class object itself, since no instance exists yet when a class method like a factory method runs — Inside a class method, self refers to the class object the message was sent to (e.g. Person), which is why [self alloc] correctly allocates an instance of whatever class the method was actually invoked on — important for subclassing.

Why can't a class method directly reference an instance variable like _name in its body? A. It technically can, it's just discouraged by convention B. Because a class method is sent to the class object, not to any particular instance, so there is no instance's storage in scope C. Because instance variables don't exist until the app finishes launching D. Because _name is always private to a different class

B. Because a class method is sent to the class object, not to any particular instance, so there is no instance's storage in scope — Instance variables are per-object storage that only exists once an instance has been allocated. A class method operates without any specific instance in scope, so referencing an instance variable directly inside one is a compiler error.

// Person.h
@interface Person : NSObject
- (instancetype)initWithName:(NSString *)name age:(NSInteger)age;
+ (instancetype)personWithName:(NSString *)name age:(NSInteger)age;
@end

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

+ (instancetype)personWithName:(NSString *)name age:(NSInteger)age {
    return [[self alloc] initWithName:name age:age];
}
@end

// Calling code:
Person *alice = [Person personWithName:@"Alice" age:30];
@interface Person : NSObject
@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) NSInteger age;

+ (NSInteger)maximumAge;                 // class method: no instance needed
- (BOOL)isOlderThan:(Person *)other;      // instance method: needs self's own age
@end

@implementation Person
+ (NSInteger)maximumAge {
    return 130;   // doesn't touch any instance's data
}

- (BOOL)isOlderThan:(Person *)other {
    return self.age > other.age;   // needs THIS instance's own age
}
@end

NSInteger cap = [Person maximumAge];             // sent to the class
BOOL olderFlag = [alice isOlderThan:bob];          // sent to a specific instance