Immutable vs. Mutable Collections
Objective-C's Foundation framework splits every core collection into an immutable base class and a mutable subclass: NSArray and NSMutableArray, NSDictionary and NSMutableDictionary, NSSet and NSMutableSet. An NSArray, once created with arrayWithObjects: or a literal like @[@"a", @"b"], can never have elements added, removed, or replaced — any attempt to call addObject: on a plain NSArray reference will fail to compile, because that method simply isn't declared on NSArray, only on NSMutableArray. This split exists deliberately for safety and performance: an immutable collection can be freely shared across threads without synchronization, and API designers use NSArray as a return type specifically to signal 'you may read this but you may not mutate it,' even when the concrete underlying object happens to be a mutable instance internally.
Cricket analogy: An NSArray is like the final printed scorecard handed to spectators after the match — fixed and unchangeable — while an NSMutableArray is like the scorer's live working sheet during play, where runs and wickets are still being added ball by ball.
NSArray *fixedFruits = @[@"apple", @"banana", @"cherry"];
// [fixedFruits addObject:@"date"]; // compile error: no visible @interface for NSArray declares addObject:
NSMutableArray *cart = [NSMutableArray arrayWithCapacity:4];
[cart addObject:@"apple"];
[cart addObject:@"banana"];
[cart removeObject:@"apple"];
NSLog(@"%@", cart); // ("banana")
NSDictionary *config = @{@"timeout": @30, @"retries": @3};
NSNumber *timeout = config[@"timeout"]; // subscripting syntax
NSMutableDictionary *scores = [NSMutableDictionary dictionary];
scores[@"Alice"] = @95;
scores[@"Bob"] = @87;
[scores removeObjectForKey:@"Bob"];Enumeration Patterns
Objective-C offers several enumeration styles with different tradeoffs. Fast enumeration (for (id obj in collection)) is compiled down to calls against NSFastEnumeration, making it the fastest and most idiomatic way to iterate when you just need each element in order and don't need the index; it also works uniformly across NSArray, NSSet, and NSDictionary (where it yields keys, not values). When you need the index alongside the object, enumerateObjectsUsingBlock: is preferable to a manual C-style for loop because it gives you both obj and idx directly and supports a stop pointer (*stop = YES;) to break out early — something fast enumeration cannot do cleanly. For NSDictionary, enumerateKeysAndObjectsUsingBlock: avoids the two-step 'get key via fast enumeration, then look up value' pattern and is measurably faster for large dictionaries because it avoids the extra hash lookup per key.
Cricket analogy: Fast enumeration is like walking down the batting order top to bottom during a straightforward run chase — simple and in order; enumerateObjectsUsingBlock: is like a match analyst who also tracks each batter's position number and can call off further analysis early (*stop = YES;) the moment the required run rate becomes irrelevant, say if rain ends the match.
Never mutate a mutable collection while fast-enumerating it (for (id obj in mutableArray) { [mutableArray removeObject:obj]; }) — this throws NSGenericException 'was mutated while being enumerated' at runtime, because fast enumeration snapshots a mutation counter and checks it hasn't changed on every iteration. If you need to remove items conditionally, either enumerate a copy, collect indices into an NSMutableIndexSet first and remove them after the loop, or use enumerateObjectsUsingBlock: with removeObjectsAtIndexes: applied afterward.
Key-Value Coding, Sorting, and Equality
NSDictionary keys are looked up via -isEqual: and -hash, which means custom objects used as keys must override both methods consistently (equal objects must produce equal hashes) or lookups will silently fail to find entries that logically should match. NSArray provides sortedArrayUsingComparator: and sortedArrayUsingDescriptors: (the latter taking NSSortDescriptor objects, useful for multi-key sorts like sorting Person objects by lastName then firstName) without mutating the original array, since NSArray itself is immutable — you always get a new sorted array back. Key-Value Coding lets you pull an array of values out of a collection of objects in one call using valueForKey:, for example [people valueForKey:@"firstName"] returns an NSArray of every person's firstName, and Foundation even supports collection operators like @avg, @sum, @count, and @max appended to a keypath for quick aggregate computations without writing a manual loop.
Cricket analogy: NSDictionary key lookup via isEqual:/hash is like matching a player to their stats using both jersey number and team name together — if you only check one attribute inconsistently, you might fail to match Virat Kohli's ODI stats to his T20 stats even though they're the same player.
- NSArray/NSDictionary/NSSet are immutable; NSMutableArray/NSMutableDictionary/NSMutableSet are their mutable subclasses that add insertion and removal methods.
- Immutable collections can be safely shared across threads without synchronization, which is why APIs often return NSArray even for internally mutable data.
- Fast enumeration (for...in) is the fastest and most idiomatic way to iterate in order; enumerateObjectsUsingBlock: adds index access and early-exit via *stop = YES.
- Mutating a mutable collection while fast-enumerating it throws a runtime exception ('was mutated while being enumerated').
- NSDictionary key lookups rely on -isEqual: and -hash; custom key objects must override both consistently.
- sortedArrayUsingDescriptors: with NSSortDescriptor supports multi-key sorting (e.g., lastName then firstName) without mutating the original array.
- Key-Value Coding's valueForKey: and collection operators (@sum, @avg, @count, @max) let you extract or aggregate values across a collection in one call.
Practice what you learned
1. What happens if you call addObject: on an NSArray reference (not NSMutableArray)?
2. Why can immutable collections like NSArray be safely shared across threads without synchronization?
3. What happens if you remove an object from a mutable array while fast-enumerating it with for...in?
4. What two methods must a custom object override to be used correctly as an NSDictionary key?
5. What does the Key-Value Coding collection operator @sum do when appended to a keypath, e.g., [people valueForKeyPath:@"salary.@sum"]?
Was this page helpful?
You May Also Like
NSString and String Handling
How Objective-C represents and manipulates text with NSString and NSMutableString, including encoding, formatting, and common gotchas.
ARC and Memory Management
How Automatic Reference Counting manages object lifetimes in Objective-C, and how it relates to the manual retain/release model it replaced.
Error Handling with NSError
The conventions and patterns Objective-C uses for reporting recoverable errors via NSError, as distinct from exceptions for programmer errors.
Related Reading
Related Study Notes in Programming
Browse all study notesApache Spark Study Notes
Programming · 30 topics
ProgrammingApache Flink Study Notes
Programming · 30 topics
ProgrammingHadoop Study Notes
Programming · 30 topics
ProgrammingSnowflake Study Notes
Programming · 30 topics
ProgrammingApache Airflow Study Notes
Programming · 30 topics
Programmingdbt (Data Build Tool) Study Notes
Programming · 30 topics