Objective-C Interview Questions with Answers
Most Asked Objective-C Interview Questions for Apple Platform Roles
Introduction
Objective‑C is the foundational language for Apple platforms, blending C with Smalltalk‑style messaging. This page compiles the most frequently asked Objective‑C interview questions – from basic syntax and object‑oriented concepts to runtime features, blocks, Grand Central Dispatch, and design patterns – essential for any iOS or macOS developer.
Why Objective-C?
- Mature ecosystem with Foundation, UIKit, and Cocoa
- Dynamic runtime – message sending, swizzling, and introspection
- Interoperability with C and C++
- Automatic Reference Counting (ARC) simplifies memory management
- Extensive legacy codebase in enterprise and production apps
- Backward compatibility with older iOS/macOS versions
Most Asked Objective-C Interview Questions
Objective-C is a general-purpose, object-oriented programming language that adds Smalltalk-style messaging to C. It is the primary language for Apple's macOS and iOS development.
- Object-oriented: Supports classes, inheritance, and polymorphism
- Dynamic runtime: Message sending, dynamic typing
- Manual memory management: Reference counting (ARC now)
- Foundation framework: Core classes and utilities
- Interoperability: Works with C and C++
// Hello World in Objective-C
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSLog(@"Hello, World!");
}
return 0;
}Variables in Objective-C can be primitive types, object types, or generic id. Objects are declared with * pointer.
- Primitive types: int, float, BOOL, NSInteger
- Object types: NSString *, NSArray *, etc.
- id: Generic object type
- Static variables:
statickeyword - Constants:
constkeyword
// Variables in Objective-C
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
@autoreleasepool {
// Mutable variable
NSString *mutableVar = @"Hello";
// Immutable (NSString is immutable)
NSString *immutableVar = @"World";
// Type inference (using compiler)
id inferred = @42; // id is a generic object type
// Primitive types
int intVal = 10;
float floatVal = 3.14f;
BOOL isActive = YES;
// Display
NSLog(@"%@", mutableVar);
NSLog(@"%@", immutableVar);
NSLog(@"%@", inferred);
NSLog(@"%d", intVal);
NSLog(@"%d", isActive);
}
return 0;
}Objective-C supports C primitive types plus Foundation framework object types.
- Primitive: int, float, double, char, BOOL
- Foundation objects: NSString, NSArray, NSDictionary
- Numbers: NSNumber, NSInteger, NSUInteger
- Collections: NSArray, NSDictionary, NSSet
- Null: nil, NSNull
// Data Types in Objective-C
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
@autoreleasepool {
// Numeric types
int intNum = 10;
float floatNum = 3.14f;
double doubleNum = 3.14159;
NSInteger integer = 100;
NSUInteger uInteger = 100;
// Boolean
BOOL isActive = YES;
BOOL isInactive = NO;
// Characters
char charVal = 'A';
// Strings
NSString *str = @"Hello Objective-C";
// Arrays (NSArray - immutable)
NSArray *array = @[@1, @2, @3, @4, @5];
// Mutable arrays (NSMutableArray)
NSMutableArray *mutableArray = [NSMutableArray arrayWithArray:@[@1, @2, @3]];
// Dictionaries (NSDictionary - immutable)
NSDictionary *dict = @{@"name": @"Alice", @"age": @25};
// Mutable dictionaries (NSMutableDictionary)
NSMutableDictionary *mutableDict = [NSMutableDictionary dictionaryWithDictionary:dict];
// NSNull (null value)
NSNull *nullValue = [NSNull null];
// Type checking
BOOL isString = [str isKindOfClass:[NSString class]];
NSLog(@"%d", isString);
}
return 0;
}Functions in Objective-C are defined using the standard C function syntax, with blocks providing lambda-like functionality.
- C functions:
int add(int a, int b) { return a + b; } - Method declaration:
- (int)add:(int)a with:(int)b; - Blocks:
int (^block)(int, int) = ^(int a, int b) { return a + b; }; - Method implementation:
- (int)add:(int)a with:(int)b { return a + b; } - Multiple return values: Using pointers or blocks
// Functions in Objective-C
#import <Foundation/Foundation.h>
// Basic function
int add(int a, int b) {
return a + b;
}
// Function with default parameters (not directly supported)
// Use overloaded methods or variadic arguments
// Function with multiple return values (using pointers)
void divide(int a, int b, int *quotient, int *remainder) {
*quotient = a / b;
*remainder = a % b;
}
// Higher-order function (using blocks)
int operate(int a, int b, int (^operation)(int, int)) {
return operation(a, b);
}
// Block (lambda equivalent)
int (^multiply)(int, int) = ^(int a, int b) {
return a * b;
};
int main(int argc, const char * argv[]) {
@autoreleasepool {
// Usage
NSLog(@"%d", add(5, 3));
int quotient, remainder;
divide(10, 3, "ient, &remainder);
NSLog(@"Quotient: %d, Remainder: %d", quotient, remainder);
// Using block
int result = operate(6, 7, ^(int a, int b) {
return a * b;
});
NSLog(@"%d", result);
// Using block variable
NSLog(@"%d", multiply(5, 3));
}
return 0;
}Objective-C provides NSArray (immutable) and NSMutableArray (mutable) for array operations, along with C-style arrays.
- NSArray:
@[@1, @2, @3] - NSMutableArray:
[NSMutableArray arrayWithArray:@[@1, @2]] - Access:
array[0]or[array objectAtIndex:0] - Modification:
[mutableArray addObject:@4] - Operations:
count,containsObject,indexOfObject
// Arrays in Objective-C
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
@autoreleasepool {
// NSArray (immutable)
NSArray *numbers = @[@1, @2, @3, @4, @5];
NSArray *strings = @[@"Apple", @"Banana", @"Orange"];
// NSMutableArray (mutable)
NSMutableArray *mutableNumbers = [NSMutableArray arrayWithArray:@[@1, @2, @3]];
// Access
NSNumber *num = numbers[2]; // Access element
NSLog(@"%@", num);
// Modify (NSMutableArray)
mutableNumbers[2] = @10;
[mutableNumbers addObject:@6]; // Add element
[mutableNumbers removeLastObject]; // Remove last
// Iteration
for (NSNumber *n in numbers) {
NSLog(@"%@", n);
}
// NSArray operations
NSUInteger count = [numbers count];
NSArray *doubled = [numbers valueForKeyPath:@"@unionOfObjects.self"];
// Display
NSLog(@"%@", numbers);
NSLog(@"%lu", (unsigned long)count);
}
return 0;
}Objective-C provides various collection classes including NSArray, NSSet, NSDictionary, and their mutable counterparts.
- NSArray: Ordered collection
- NSSet: Unordered collection, no duplicates
- NSDictionary: Key-value pairs
- Mutable versions: NSMutableArray, NSMutableSet, NSMutableDictionary
- Operations: Filtering, sorting, enumeration
// Collections in Objective-C
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
@autoreleasepool {
// NSArray (immutable)
NSArray *immutableArray = @[@1, @2, @3, @4, @5];
// NSMutableArray (mutable)
NSMutableArray *mutableArray = [NSMutableArray arrayWithArray:@[@1, @2, @3]];
[mutableArray addObject:@4];
[mutableArray removeObject:@2];
// NSSet (immutable, unique)
NSSet *immutableSet = [NSSet setWithArray:@[@1, @2, @3]];
// NSMutableSet (mutable)
NSMutableSet *mutableSet = [NSMutableSet setWithArray:@[@1, @2, @3]];
[mutableSet addObject:@4];
// NSDictionary (immutable)
NSDictionary *immutableDict = @{@"key1": @"value1", @"key2": @"value2"};
// NSMutableDictionary (mutable)
NSMutableDictionary *mutableDict = [NSMutableDictionary dictionaryWithDictionary:immutableDict];
mutableDict[@"key3"] = @"value3";
[mutableDict removeObjectForKey:@"key1"];
// Collection operations
NSArray *numbers = @[@1, @2, @3, @4, @5, @6];
NSArray *evens = [numbers filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"self %% 2 == 0"]];
NSArray *doubled = [numbers valueForKeyPath:@"@unionOfObjects.self"];
NSNumber *sum = [numbers valueForKeyPath:@"@sum.self"];
NSLog(@"%@", evens);
NSLog(@"%@", doubled);
NSLog(@"%@", sum);
}
return 0;
}Objective-C uses classes with properties and methods as data containers, similar to data classes in other languages.
- Class definition:
@interface Person : NSObject - Properties:
@property (nonatomic, strong) NSString *name; - Initializer:
- (instancetype)initWithName:(NSString *)name; - Copying: Implement
NSCopyingprotocol - Description: Override
descriptionmethod
// Data Classes in Objective-C (using Classes)
#import <Foundation/Foundation.h>
// Interface declaration
@interface Person : NSObject
@property (nonatomic, strong) NSString *name;
@property (nonatomic, assign) NSInteger age;
@property (nonatomic, strong) NSString *city;
- (instancetype)initWithName:(NSString *)name age:(NSInteger)age city:(NSString *)city;
- (id)copyWithZone:(NSZone *)zone;
@end
// Implementation
@implementation Person
- (instancetype)initWithName:(NSString *)name age:(NSInteger)age city:(NSString *)city {
self = [super init];
if (self) {
_name = name;
_age = age;
_city = city ?: @"Unknown";
}
return self;
}
- (id)copyWithZone:(NSZone *)zone {
Person *copy = [[Person allocWithZone:zone] initWithName:self.name age:self.age city:self.city];
return copy;
}
- (NSString *)description {
return [NSString stringWithFormat:@"Person(name=%@, age=%ld, city=%@)", self.name, (long)self.age, self.city];
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
Person *person1 = [[Person alloc] initWithName:@"Alice" age:25 city:@"NYC"];
Person *person2 = [person1 copy];
person2.age = 26;
NSLog(@"%@", person1);
NSLog(@"%@", person2);
NSLog(@"Name: %@", person1.name);
NSLog(@"Age: %ld", (long)person1.age);
}
return 0;
}Objective-C doesn't have sealed classes, but similar functionality can be achieved using protocols and class clusters.
- Protocols: Define interfaces
- Class clusters: Private subclasses
- Abstract classes: Using NSObject
- Type checking:
isKindOfClass - Protocol adoption:
@protocol Result
// Protocol-based Sealed Classes in Objective-C
#import <Foundation/Foundation.h>
// Protocol as interface
@protocol Result <NSObject>
@end
// Concrete implementations
@interface Success : NSObject <Result>
@property (nonatomic, strong) NSString *data;
- (instancetype)initWithData:(NSString *)data;
@end
@interface Error : NSObject <Result>
@property (nonatomic, strong) NSString *message;
- (instancetype)initWithMessage:(NSString *)message;
@end
@interface Loading : NSObject <Result>
@end
// Shape protocol
@protocol Shape <NSObject>
- (double)area;
@end
// Circle implementation
@interface Circle : NSObject <Shape>
@property (nonatomic, assign) double radius;
- (instancetype)initWithRadius:(double)radius;
@end
// Rectangle implementation
@interface Rectangle : NSObject <Shape>
@property (nonatomic, assign) double width;
@property (nonatomic, assign) double height;
- (instancetype)initWithWidth:(double)width height:(double)height;
@end
// Point implementation
@interface Point : NSObject <Shape>
@end
// Implementations
@implementation Success
- (instancetype)initWithData:(NSString *)data {
self = [super init];
if (self) {
_data = data;
}
return self;
}
@end
@implementation Error
- (instancetype)initWithMessage:(NSString *)message {
self = [super init];
if (self) {
_message = message;
}
return self;
}
@end
@implementation Loading
@end
@implementation Circle
- (instancetype)initWithRadius:(double)radius {
self = [super init];
if (self) {
_radius = radius;
}
return self;
}
- (double)area {
return M_PI * self.radius * self.radius;
}
@end
@implementation Rectangle
- (instancetype)initWithWidth:(double)width height:(double)height {
self = [super init];
if (self) {
_width = width;
_height = height;
}
return self;
}
- (double)area {
return self.width * self.height;
}
@end
@implementation Point
- (double)area {
return 0.0;
}
@end
// Helper function
NSString* handleResult(id<Result> result) {
if ([result isKindOfClass:[Success class]]) {
Success *success = (Success *)result;
return [NSString stringWithFormat:@"Success: %@", success.data];
} else if ([result isKindOfClass:[Error class]]) {
Error *error = (Error *)result;
return [NSString stringWithFormat:@"Error: %@", error.message];
} else if ([result isKindOfClass:[Loading class]]) {
return @"Loading...";
}
return @"Unknown";
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
id<Result> result = [[Success alloc] initWithData:@"Data loaded"];
NSLog(@"%@", handleResult(result));
Circle *circle = [[Circle alloc] initWithRadius:5.0];
NSLog(@"Circle area: %f", [circle area]);
}
return 0;
}Objective-C uses nil for null values. Sending messages to nil is safe and returns 0 or nil.
- nil: Represents null object
- Nil messaging: Safe to send messages to nil
- NSNull: Used in collections for null values
- Null checks:
if (object == nil) - Optional annotations:
__nullable,__nonnull
// Null Safety in Objective-C
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
@autoreleasepool {
// nil (null) values
NSString *nullableString = nil;
NSString *nonNullableString = @"Hello";
// Check for nil
if (nullableString == nil) {
NSLog(@"String is nil");
}
// Safe access (Objective-C sends messages to nil safely)
NSUInteger length = [nullableString length]; // Returns 0
NSLog(@"Length: %lu", (unsigned long)length);
// Elvis operator equivalent
NSString *result = nullableString ?: @"default";
NSLog(@"%@", result);
// Safe method call with nil check
if (nullableString) {
NSLog(@"String is: %@", nullableString);
NSLog(@"Length: %lu", (unsigned long)[nullableString length]);
}
// NSNull for dictionary values
NSDictionary *dict = @{@"key": [NSNull null]};
id value = dict[@"key"];
if (value == [NSNull null]) {
NSLog(@"Value is null");
}
// Optional using __nullable annotation (for Swift interop)
// NSString * __nullable optionalString = nil;
}
return 0;
}Objective-C supports standard C control flow statements including if-else, switch, for, while, and do-while loops.
- If-else:
if (condition) { } else { } - Switch:
switch (value) { case: ... } - For loop:
for (int i = 0; i < n; i++) - For-in:
for (id item in array) - While:
while (condition) { }
// Control Flow in Objective-C
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
@autoreleasepool {
// If-else
NSInteger age = 25;
NSString *status;
if (age < 18) {
status = @"Minor";
} else {
status = @"Adult";
}
NSLog(@"%@", status);
// Switch statement
char grade = 'A';
NSString *result;
switch (grade) {
case 'A':
result = @"Excellent";
break;
case 'B':
result = @"Good";
break;
case 'C':
result = @"Fair";
break;
default:
result = @"Needs Improvement";
break;
}
NSLog(@"%@", result);
// For loop
for (int i = 0; i < 5; i++) {
NSLog(@"%d", i);
}
// For loop with step
for (int i = 1; i <= 10; i += 2) {
NSLog(@"%d", i);
}
// For-in loop (Fast enumeration)
NSArray *items = @[@"A", @"B", @"C"];
for (NSString *item in items) {
NSLog(@"%@", item);
}
// While loop
int i = 0;
while (i < 5) {
NSLog(@"%d", i);
i++;
}
// Do-while loop
i = 0;
do {
NSLog(@"%d", i);
i--;
} while (i > 0);
}
return 0;
}Objective-C supports object-oriented programming with classes, inheritance, method overriding, and protocols.
- Class definition:
@interface ClassName : SuperClass - Method overriding: Redefine method in subclass
- Protocols: Similar to interfaces
- Categories: Add methods to existing classes
- Multiple inheritance: Achieved through protocols
// Classes and Inheritance in Objective-C
#import <Foundation/Foundation.h>
// Base class
@interface Animal : NSObject
@property (nonatomic, strong) NSString *name;
- (instancetype)initWithName:(NSString *)name;
- (void)makeSound;
@end
// Derived class
@interface Dog : Animal
@property (nonatomic, strong) NSString *breed;
- (instancetype)initWithName:(NSString *)name breed:(NSString *)breed;
- (void)makeSound; // Override
@end
// Abstract class (using NSObject)
@interface Vehicle : NSObject
- (void)start; // Abstract method
- (void)stop;
@end
// Interface (protocol)
@protocol Flyable <NSObject>
- (void)fly;
@optional
- (void)land;
@end
@protocol Swimmable <NSObject>
- (void)swim;
@end
// Implementation
@implementation Animal
- (instancetype)initWithName:(NSString *)name {
self = [super init];
if (self) {
_name = name;
}
return self;
}
- (void)makeSound {
NSLog(@"Animal sound");
}
@end
@implementation Dog
- (instancetype)initWithName:(NSString *)name breed:(NSString *)breed {
self = [super initWithName:name];
if (self) {
_breed = breed;
}
return self;
}
- (void)makeSound {
NSLog(@"Woof!");
}
@end
// Duck class implementing protocols
@interface Duck : NSObject <Flyable, Swimmable>
@end
@implementation Duck
- (void)fly {
NSLog(@"Flying");
}
- (void)swim {
NSLog(@"Swimming");
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
Dog *dog = [[Dog alloc] initWithName:@"Rex" breed:@"German Shepherd"];
[dog makeSound];
NSLog(@"%@", dog.name);
NSLog(@"%@", dog.breed);
Duck *duck = [[Duck alloc] init];
[duck fly];
[duck swim];
}
return 0;
}Properties are declared using @property and automatically generate getter and setter methods with memory management attributes.
- @property: Declare property
- Attributes:
strong,weak,assign,copy - Custom getter/setter: Implement manually
- Read-only:
readonlyattribute - Synthesize:
@synthesize(modern compilers auto-synthesize)
// Properties in Objective-C
#import <Foundation/Foundation.h>
@interface Person : NSObject
// Properties with attributes
@property (nonatomic, strong) NSString *name;
@property (nonatomic, assign) NSInteger age;
@property (nonatomic, strong) NSString *email;
@property (nonatomic, strong) NSString *address;
// Read-only property
@property (nonatomic, readonly) NSString *fullName;
// Lazy property (custom getter)
@property (nonatomic, strong) NSString *expensiveData;
// Custom getter/setter
- (NSString *)name;
- (void)setName:(NSString *)name;
@end
@implementation Person {
NSString *_name;
NSInteger _age;
NSString *_email;
NSString *_address;
NSString *_expensiveData;
}
- (instancetype)init {
self = [super init];
if (self) {
_name = @"";
_age = 0;
_email = @"";
_address = @"";
}
return self;
}
// Custom getter for name
- (NSString *)name {
return [_name uppercaseString];
}
// Custom setter for name
- (void)setName:(NSString *)name {
_name = [name stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
}
// Custom setter for age (validation)
- (void)setAge:(NSInteger)age {
if (age >= 0) {
_age = age;
}
}
// Read-only property
- (NSString *)fullName {
return [NSString stringWithFormat:@"%@ (Age: %ld)", self.name, (long)self.age];
}
// Lazy property
- (NSString *)expensiveData {
if (!_expensiveData) {
NSLog(@"Computing expensive data...");
_expensiveData = @"Expensive Result";
}
return _expensiveData;
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
Person *person = [[Person alloc] init];
person.name = @" Alice ";
NSLog(@"%@", person.name); // ALICE
person.age = 25;
NSLog(@"%ld", (long)person.age);
NSLog(@"%@", person.fullName);
NSLog(@"%@", person.expensiveData); // Computes
NSLog(@"%@", person.expensiveData); // Returns cached
}
return 0;
}Class methods are declared with + and are called on the class itself. They can access class variables and create factory methods.
- + methods: Class-level methods
- Singleton pattern:
sharedInstancemethod - Factory methods:
+ (instancetype)create - Class variables: Static variables
- dispatch_once: Thread-safe initialization
// Class Methods in Objective-C
#import <Foundation/Foundation.h>
@interface MyClass : NSObject
// Class variable (static variable)
+ (NSInteger)counter;
+ (void)incrementCounter;
// Class constants
+ (NSString *)tag;
// Factory method
+ (instancetype)create;
// Class method
+ (void)classMethod;
// Instance method
- (void)instanceMethod;
@end
@implementation MyClass {
NSInteger _instanceCounter;
}
static NSInteger _counter = 0;
+ (NSInteger)counter {
return _counter;
}
+ (void)incrementCounter {
_counter++;
}
+ (NSString *)tag {
return @"MyClass";
}
+ (instancetype)create {
return [[self alloc] init];
}
+ (void)classMethod {
NSLog(@"Class method called, counter: %ld", (long)[self counter]);
}
- (instancetype)init {
self = [super init];
if (self) {
_instanceCounter = ++_counter;
}
return self;
}
- (void)instanceMethod {
NSLog(@"Instance method called, instance: %ld", (long)_instanceCounter);
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSLog(@"%@", [MyClass tag]);
MyClass *obj1 = [MyClass create];
MyClass *obj2 = [MyClass create];
NSLog(@"Counter: %ld", (long)[MyClass counter]);
[MyClass classMethod];
[obj1 instanceMethod];
[obj2 instanceMethod];
}
return 0;
}Objective-C uses @try, @catch, @finally for exception handling. Custom exceptions can be created by subclassing NSException.
- @try-@catch: Handle exceptions
- @finally: Cleanup block
- NSException: Base exception class
- @throw: Raise exception
- Custom exceptions: Subclass NSException
// Exception Handling in Objective-C
#import <Foundation/Foundation.h>
// Custom exception
@interface InvalidAgeException : NSException
@property (nonatomic, assign) NSInteger age;
+ (instancetype)exceptionWithAge:(NSInteger)age;
@end
@implementation InvalidAgeException
+ (instancetype)exceptionWithAge:(NSInteger)age {
InvalidAgeException *exception = [super exceptionWithName:@"InvalidAgeException"
reason:[NSString stringWithFormat:@"Invalid age: %ld", (long)age]
userInfo:@{@"age": @(age)}];
exception.age = age;
return exception;
}
@end
// Try-catch block
NSInteger divide(NSInteger a, NSInteger b) {
@try {
return a / b;
} @catch (NSException *exception) {
if ([exception.name isEqualToString:@"NSInvalidArgumentException"]) {
NSLog(@"Division by zero!");
return 0;
}
@throw;
}
}
void validateAge(NSInteger age) {
if (age < 0 || age > 150) {
@throw [InvalidAgeException exceptionWithAge:age];
}
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
// Try-catch
@try {
NSLog(@"%ld", (long)divide(10, 2));
NSLog(@"%ld", (long)divide(10, 0));
} @catch (NSException *exception) {
NSLog(@"Error: %@", exception.reason);
} @finally {
NSLog(@"Finally block");
}
// Try-catch for custom exception
@try {
validateAge(200);
} @catch (InvalidAgeException *exception) {
NSLog(@"Caught: %@, Age: %ld", exception.reason, (long)exception.age);
}
// Use @finally for cleanup
@try {
// Some operation
} @catch (NSException *exception) {
NSLog(@"Error: %@", exception);
} @finally {
NSLog(@"Cleaning up resources...");
}
}
return 0;
}Blocks are anonymous functions that capture variables from their scope. They are similar to lambdas in other languages.
- Block syntax:
^returnType(parameters) - Capturing variables: Read-only by default
- __block: Modifiable captured variables
- Typedef:
typedef void (^BlockType)(void) - GCD: Used with Grand Central Dispatch
// Blocks in Objective-C
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
@autoreleasepool {
// Basic block
int (^square)(int) = ^(int x) {
return x * x;
};
// Block with multiple parameters
int (^add)(int, int) = ^(int a, int b) {
return a + b;
};
// Block as parameter
int result = [self performOperation:10 y:20 operation:^int(int a, int b) {
return a * b;
}];
NSLog(@"%d", result);
// Block with multiple lines
int (^complexOperation)(int) = ^(int x) {
int y = x * 2;
return y + 10;
};
// Using block with array
NSArray *numbers = @[@1, @2, @3, @4, @5];
NSArray *doubled = [numbers valueForKeyPath:@"@unionOfObjects.self"];
// Block with enumeration
[numbers enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
NSLog(@"Index %lu: %@", (unsigned long)idx, obj);
}];
// Block with completion handler
void (^completionHandler)(NSString *, NSError *) = ^(NSString *result, NSError *error) {
if (error) {
NSLog(@"Error: %@", error);
} else {
NSLog(@"Result: %@", result);
}
};
completionHandler(@"Success", nil);
}
return 0;
}
// Helper function
int performOperation(int x, int y, int (^operation)(int, int)) {
return operation(x, y);
}Categories add methods to existing classes without subclassing. They are a powerful feature for extending functionality.
- Category syntax:
@interface NSString (Extensions) - Adding methods: Add instance and class methods
- Method overriding: Can override existing methods
- Private methods: Use categories for private methods
- Class extensions:
@interface ClassName ()
// Categories (Extension Functions) in Objective-C
#import <Foundation/Foundation.h>
// NSString category
@interface NSString (StringExtensions)
- (BOOL)isEmail;
- (NSString *)addPrefix:(NSString *)prefix;
- (NSUInteger)wordCount;
@end
@implementation NSString (StringExtensions)
- (BOOL)isEmail {
return [self containsString:@"@"] && [self containsString:@"."];
}
- (NSString *)addPrefix:(NSString *)prefix {
return [prefix stringByAppendingString:self];
}
- (NSUInteger)wordCount {
NSArray *words = [self componentsSeparatedByString:@" "];
return words.count;
}
@end
// NSNumber category
@interface NSNumber (NumberExtensions)
- (BOOL)isEven;
- (BOOL)isOdd;
@end
@implementation NSNumber (NumberExtensions)
- (BOOL)isEven {
return [self integerValue] % 2 == 0;
}
- (BOOL)isOdd {
return [self integerValue] % 2 != 0;
}
@end
// NSArray category
@interface NSArray (ArrayExtensions)
- (id)secondOrNil;
@end
@implementation NSArray (ArrayExtensions)
- (id)secondOrNil {
if (self.count >= 2) {
return self[1];
}
return nil;
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSString *email = @"test@example.com";
NSLog(@"%d", [email isEmail]); // 1 (true)
NSString *greeting = [@"Hello" addPrefix:@"Greeting: "];
NSLog(@"%@", greeting);
NSNumber *num = @5;
NSLog(@"%d", [num isEven]); // 0 (false)
NSString *sentence = @"Hello World";
NSLog(@"%lu", (unsigned long)[sentence wordCount]);
NSArray *array = @[@1, @2, @3];
NSLog(@"%@", [array secondOrNil]);
}
return 0;
}Protocols define interfaces that classes can adopt. They are similar to interfaces in Java or traits in other languages.
- @protocol: Define protocol
- @required: Required methods
- @optional: Optional methods
- Adoption:
@interface Class : SuperClass <Protocol> - Delegation: Common use pattern
// Protocols in Objective-C
#import <Foundation/Foundation.h>
// Protocol definition
@protocol Printable <NSObject>
@required
- (void)print;
@optional
- (void)printWithPrefix:(NSString *)prefix;
@end
// Protocol with multiple methods
@protocol DataSource <NSObject>
- (NSInteger)numberOfItems;
- (id)itemAtIndex:(NSInteger)index;
@end
// Class implementing protocol
@interface MyDocument : NSObject <Printable, DataSource>
@property (nonatomic, strong) NSArray *items;
@end
@implementation MyDocument
- (instancetype)init {
self = [super init];
if (self) {
_items = @[@"Item 1", @"Item 2", @"Item 3"];
}
return self;
}
- (void)print {
NSLog(@"Printing document");
}
- (void)printWithPrefix:(NSString *)prefix {
NSLog(@"%@: Printing document", prefix);
}
- (NSInteger)numberOfItems {
return self.items.count;
}
- (id)itemAtIndex:(NSInteger)index {
return self.items[index];
}
@end
// Protocol for delegation
@protocol MyDelegate <NSObject>
- (void)didFinishTask;
- (void)didFailWithError:(NSError *)error;
@end
// Class using delegate
@interface TaskManager : NSObject
@property (nonatomic, weak) id<MyDelegate> delegate;
- (void)startTask;
@end
@implementation TaskManager
- (void)startTask {
// Simulate task
BOOL success = YES;
if (success) {
[self.delegate didFinishTask];
} else {
NSError *error = [NSError errorWithDomain:@"TaskDomain" code:1 userInfo:@{NSLocalizedDescriptionKey: @"Task failed"}];
[self.delegate didFailWithError:error];
}
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
MyDocument *doc = [[MyDocument alloc] init];
[doc print];
[doc printWithPrefix:@"PREFIX"];
NSLog(@"%ld", (long)[doc numberOfItems]);
NSLog(@"%@", [doc itemAtIndex:1]);
}
return 0;
}Type aliases in Objective-C are created using typedef, allowing aliases for blocks, structs, and complex types.
- typedef: Create type alias
- Block aliases:
typedef void (^CompletionBlock)(id result) - Struct aliases:
typedef struct { } MyStruct - Complex types:
typedef NSDictionary<NSString *, id> UserMap - Enum aliases:
typedef NS_ENUM(NSInteger, MyEnum)
// Type Aliases in Objective-C
#import <Foundation/Foundation.h>
// Type alias using typedef
typedef int (^Operation)(int, int);
typedef NSDictionary<NSString *, id> UserMap;
typedef void (^ResultCallback)(id result, NSError *error);
// Using type alias
Operation add = ^(int a, int b) {
return a + b;
};
Operation multiply = ^(int a, int b) {
return a * b;
};
int execute(Operation op, int a, int b) {
return op(a, b);
}
// Complex type alias
typedef struct {
NSString *name;
NSInteger age;
} User;
// Usage
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSLog(@"%d", execute(add, 5, 3));
NSLog(@"%d", execute(multiply, 5, 3));
UserMap *users = @{
@"user1": @{@"name": @"Alice", @"age": @25},
@"user2": @{@"name": @"Bob", @"age": @30}
};
NSLog(@"%@", users[@"user1"][@"name"]);
// Using callback
ResultCallback callback = ^(id result, NSError *error) {
if (error) {
NSLog(@"Error: %@", error);
} else {
NSLog(@"Result: %@", result);
}
};
callback(@"Success", nil);
}
return 0;
}Inline functions in Objective-C are implemented using static inline or macros for performance-critical operations.
- static inline:
static inline int square(int x) { return x*x; } - Macros:
#define SQUARE(x) ((x)*(x)) - Performance: Reduced function call overhead
- Compile-time: Macros expanded at compile time
- Type safety: Inline functions are type-safe
// Inline Functions in Objective-C
#import <Foundation/Foundation.h>
// Inline function using static inline
static inline int square(int x) {
return x * x;
}
static inline int add(int a, int b) {
return a + b;
}
// Macro for inline (preprocessor)
#define SQUARE(x) ((x) * (x))
#define ADD(a, b) ((a) + (b))
// Function-like macro with multiple statements
#define MEASURE_TIME(block) do { NSDate *start = [NSDate date]; block(); double timeInterval = [[NSDate date] timeIntervalSinceDate:start]; NSLog(@"Time: %f seconds", timeInterval); } while(0)
// Inline function with block
typedef void (^Block)(void);
static inline void measureTime(Block block) {
NSDate *start = [NSDate date];
block();
double timeInterval = [[NSDate date] timeIntervalSinceDate:start];
NSLog(@"Time: %f seconds", timeInterval);
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
// Using inline function
NSLog(@"%d", square(5));
NSLog(@"%d", add(5, 3));
// Using macro
NSLog(@"%d", SQUARE(5));
NSLog(@"%d", ADD(5, 3));
// Using measure time macro
MEASURE_TIME(^{
[NSThread sleepForTimeInterval:0.1];
NSLog(@"Operation completed");
});
// Using inline function with block
measureTime(^{
[NSThread sleepForTimeInterval:0.1];
NSLog(@"Operation completed");
});
}
return 0;
}Higher-order functions in Objective-C are implemented using blocks. They can take blocks as parameters or return blocks.
- Block parameters:
void (^block)(void) - Returning blocks:
int (^getMultiplier(int factor))(int) - Array operations:
enumerateObjectsUsingBlock - GCD:
dispatch_asyncwith blocks - Composition: Combining blocks
// Higher-Order Functions in Objective-C
#import <Foundation/Foundation.h>
// Function that takes a block as parameter
int applyOperation(int a, int b, int (^operation)(int, int)) {
return operation(a, b);
}
// Function that returns a block
int (^getMultiplier(int factor))(int) {
return ^(int x) {
return x * factor;
};
}
// Block composition
int (^compose(int (^f)(int), int (^g)(int)))(int) {
return ^(int x) {
return f(g(x));
};
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
// Using block as parameter
int result = applyOperation(10, 20, ^(int a, int b) {
return a + b;
});
NSLog(@"%d", result);
// Getting a block from a function
int (^doubleBlock)(int) = getMultiplier(2);
NSLog(@"%d", doubleBlock(5));
// Block composition
int (^square)(int) = ^(int x) {
return x * x;
};
int (^addTen)(int) = ^(int x) {
return x + 10;
};
int (^squareThenAddTen)(int) = compose(addTen, square);
NSLog(@"%d", squareThenAddTen(5));
// Using named function with block
int (^addBlock)(int, int) = ^(int a, int b) {
return a + b;
};
NSLog(@"%d", applyOperation(10, 20, addBlock));
// Array operations with blocks
NSArray *numbers = @[@1, @2, @3, @4, @5];
NSArray *squared = [numbers valueForKeyPath:@"@unionOfObjects.self"];
NSLog(@"%@", squared);
}
return 0;
}GCD is Apple's concurrency framework for managing queues and threads. It provides efficient concurrent execution of tasks.
- Dispatch queues: Serial and concurrent
- Main queue:
dispatch_get_main_queue() - Global queues:
dispatch_get_global_queue() - Async tasks:
dispatch_async - Dispatch groups:
dispatch_group_t
// Grand Central Dispatch (GCD) in Objective-C
#import <Foundation/Foundation.h>
// Async dispatch
void fetchData(void (^completion)(NSString *)) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[NSThread sleepForTimeInterval:1.0];
completion(@"Data loaded");
});
}
// Multiple async tasks
void parallelTasks(void (^completion)(NSArray *)) {
dispatch_group_t group = dispatch_group_create();
__block NSString *result1 = nil;
__block NSString *result2 = nil;
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[NSThread sleepForTimeInterval:1.0];
result1 = @"Task 1";
});
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[NSThread sleepForTimeInterval:0.5];
result2 = @"Task 2";
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
completion(@[result1, result2]);
});
}
// Timeout
void withTimeout(NSString * (^block)(void)) {
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
__block NSString *result = nil;
dispatch_async(queue, ^{
result = block();
dispatch_semaphore_signal(semaphore);
});
dispatch_time_t timeout = dispatch_time(DISPATCH_TIME_NOW, 1.0 * NSEC_PER_SEC);
if (dispatch_semaphore_wait(semaphore, timeout) != 0) {
NSLog(@"Timed out!");
} else {
NSLog(@"Result: %@", result);
}
}
// Serial queue
void serialQueueExample(void) {
dispatch_queue_t serialQueue = dispatch_queue_create("com.example.serial", DISPATCH_QUEUE_SERIAL);
dispatch_async(serialQueue, ^{
[NSThread sleepForTimeInterval:0.5];
NSLog(@"Task 1 completed");
});
dispatch_async(serialQueue, ^{
[NSThread sleepForTimeInterval:0.3];
NSLog(@"Task 2 completed");
});
}
// Concurrent queue
void concurrentQueueExample(void) {
dispatch_queue_t concurrentQueue = dispatch_queue_create("com.example.concurrent", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(concurrentQueue, ^{
[NSThread sleepForTimeInterval:0.5];
NSLog(@"Concurrent task 1 completed");
});
dispatch_async(concurrentQueue, ^{
[NSThread sleepForTimeInterval:0.3];
NSLog(@"Concurrent task 2 completed");
});
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
// Async fetch
fetchData(^(NSString *result) {
NSLog(@"%@", result);
});
// Parallel tasks
parallelTasks(^(NSArray *results) {
NSLog(@"Results: %@", results);
});
// Timeout example
withTimeout(^NSString * {
[NSThread sleepForTimeInterval:0.5];
return @"Success";
});
// Queue examples
serialQueueExample();
concurrentQueueExample();
// Keep main thread alive
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:3.0]];
}
return 0;
}NSNotificationCenter is a system for broadcasting notifications and implementing the observer pattern.
- Notification center:
[NSNotificationCenter defaultCenter] - Add observer:
addObserver:selector:name:object: - Post notification:
postNotificationName:object:userInfo: - Remove observer:
removeObserver: - Custom notifications: Define notification names
// NSNotificationCenter (Observer Pattern)
#import <Foundation/Foundation.h>
// Notification names
NSString * const MyNotification = @"MyNotification";
NSString * const UserDataKey = @"UserDataKey";
// Observer class
@interface MyObserver : NSObject
- (void)handleNotification:(NSNotification *)notification;
@end
@implementation MyObserver
- (instancetype)init {
self = [super init];
if (self) {
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(handleNotification:)
name:MyNotification
object:nil];
}
return self;
}
- (void)handleNotification:(NSNotification *)notification {
NSDictionary *userInfo = notification.userInfo;
NSString *data = userInfo[UserDataKey];
NSLog(@"Received notification with data: %@", data);
}
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
@end
// Multiple observers
@interface MultiObserver : NSObject
- (void)handleNotificationA:(NSNotification *)notification;
- (void)handleNotificationB:(NSNotification *)notification;
@end
@implementation MultiObserver
- (instancetype)init {
self = [super init];
if (self) {
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(handleNotificationA:)
name:@"NotificationA"
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(handleNotificationB:)
name:@"NotificationB"
object:nil];
}
return self;
}
- (void)handleNotificationA:(NSNotification *)notification {
NSLog(@"Received Notification A");
}
- (void)handleNotificationB:(NSNotification *)notification {
NSLog(@"Received Notification B");
}
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
MyObserver *observer = [[MyObserver alloc] init];
MultiObserver *multiObserver = [[MultiObserver alloc] init];
// Post notifications
[[NSNotificationCenter defaultCenter] postNotificationName:MyNotification
object:nil
userInfo:@{UserDataKey: @"Hello World"}];
[[NSNotificationCenter defaultCenter] postNotificationName:@"NotificationA" object:nil];
[[NSNotificationCenter defaultCenter] postNotificationName:@"NotificationB" object:nil];
// Keep main thread alive
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.5]];
}
return 0;
}KVO allows objects to be notified when properties of other objects change. It's a form of the observer pattern.
- Add observer:
addObserver:forKeyPath:options:context: - Observe method:
observeValueForKeyPath:ofObject:change:context: - Remove observer:
removeObserver:forKeyPath: - Options:
NSKeyValueObservingOptionNew,NSKeyValueObservingOptionOld - Automatic notifications:
willChangeValueForKey:,didChangeValueForKey:
// Key-Value Observing (KVO) in Objective-C
#import <Foundation/Foundation.h>
// Observable class
@interface Person : NSObject
@property (nonatomic, strong) NSString *name;
@property (nonatomic, assign) NSInteger age;
@end
@implementation Person
@end
// Observer class
@interface PersonObserver : NSObject
- (void)startObservingPerson:(Person *)person;
- (void)stopObservingPerson:(Person *)person;
@end
@implementation PersonObserver
- (void)startObservingPerson:(Person *)person {
[person addObserver:self
forKeyPath:@"name"
options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld
context:nil];
[person addObserver:self
forKeyPath:@"age"
options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld
context:nil];
}
- (void)stopObservingPerson:(Person *)person {
[person removeObserver:self forKeyPath:@"name"];
[person removeObserver:self forKeyPath:@"age"];
}
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary<NSKeyValueChangeKey,id> *)change
context:(void *)context {
if ([keyPath isEqualToString:@"name"]) {
NSString *oldValue = change[NSKeyValueChangeOldKey];
NSString *newValue = change[NSKeyValueChangeNewKey];
NSLog(@"Name changed from %@ to %@", oldValue, newValue);
} else if ([keyPath isEqualToString:@"age"]) {
NSNumber *oldValue = change[NSKeyValueChangeOldKey];
NSNumber *newValue = change[NSKeyValueChangeNewKey];
NSLog(@"Age changed from %ld to %ld", (long)[oldValue integerValue], (long)[newValue integerValue]);
}
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
Person *person = [[Person alloc] init];
PersonObserver *observer = [[PersonObserver alloc] init];
[observer startObservingPerson:person];
person.name = @"Alice";
person.age = 25;
person.name = @"Bob";
person.age = 30;
[observer stopObservingPerson:person];
// Keep main thread alive
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.5]];
}
return 0;
}KVC allows accessing object properties by name using string keys, enabling dynamic property access.
- valueForKey:: Get property value
- setValue:forKey:: Set property value
- Collection operators:
@sum,@avg,@max - Key paths:
@"employees.@sum.salary" - Validation:
validateValue:forKey:
// Key-Value Coding (KVC) in Objective-C
#import <Foundation/Foundation.h>
// Class with properties
@interface Employee : NSObject
@property (nonatomic, strong) NSString *name;
@property (nonatomic, assign) NSInteger age;
@property (nonatomic, assign) double salary;
@property (nonatomic, strong) NSString *department;
@end
@implementation Employee
@end
// Class with nested objects
@interface Department : NSObject
@property (nonatomic, strong) NSString *name;
@property (nonatomic, strong) NSArray *employees;
@end
@implementation Department
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
// Create employees
Employee *emp1 = [[Employee alloc] init];
[emp1 setValue:@"Alice" forKey:@"name"];
[emp1 setValue:@25 forKey:@"age"];
[emp1 setValue:@50000 forKey:@"salary"];
[emp1 setValue:@"Engineering" forKey:@"department"];
Employee *emp2 = [[Employee alloc] init];
[emp2 setValue:@"Bob" forKey:@"name"];
[emp2 setValue:@30 forKey:@"age"];
[emp2 setValue:@60000 forKey:@"salary"];
[emp2 setValue:@"Engineering" forKey:@"department"];
// Department
Department *dept = [[Department alloc] init];
[dept setValue:@"Engineering" forKey:@"name"];
[dept setValue:@[emp1, emp2] forKey:@"employees"];
// KVC access
NSLog(@"Employee name: %@", [emp1 valueForKey:@"name"]);
NSLog(@"Department name: %@", [dept valueForKey:@"name"]);
// KVC collection operators
NSNumber *totalSalary = [dept valueForKeyPath:@"employees.@sum.salary"];
NSNumber *avgSalary = [dept valueForKeyPath:@"employees.@avg.salary"];
NSNumber *maxSalary = [dept valueForKeyPath:@"employees.@max.salary"];
NSNumber *minSalary = [dept valueForKeyPath:@"employees.@min.salary"];
NSLog(@"Total salary: %@", totalSalary);
NSLog(@"Average salary: %@", avgSalary);
NSLog(@"Max salary: %@", maxSalary);
NSLog(@"Min salary: %@", minSalary);
// Array of values
NSArray *names = [dept valueForKeyPath:@"employees.name"];
NSLog(@"Names: %@", names);
}
return 0;
}Objective-C supports lightweight generics (introduced in Xcode 7) for type safety in collections and custom classes.
- Generic syntax:
NSArray<NSString *> - Custom generics:
@interface Box<ObjectType> - Type safety: Compile-time type checking
- Collection types:
NSArray<NSString *> - Limitations: Runtime type erasure
// Generics in Objective-C
#import <Foundation/Foundation.h>
// Generic class (using lightweight generics)
@interface Box<ObjectType> : NSObject
@property (nonatomic, strong) ObjectType value;
- (instancetype)initWithValue:(ObjectType)value;
- (ObjectType)getValue;
@end
@implementation Box
- (instancetype)initWithValue:(id)value {
self = [super init];
if (self) {
_value = value;
}
return self;
}
- (id)getValue {
return self.value;
}
@end
// Generic method (using instancetype)
@interface ArrayUtils : NSObject
+ (NSArray *)reverseArray:(NSArray *)array;
+ (id)firstElement:(NSArray *)array;
@end
@implementation ArrayUtils
+ (NSArray *)reverseArray:(NSArray *)array {
return [[array reverseObjectEnumerator] allObjects];
}
+ (id)firstElement:(NSArray *)array {
return array.firstObject;
}
@end
// Generic collection
@interface GenericDictionary<KeyType, ObjectType> : NSObject
@property (nonatomic, strong) NSMutableDictionary *dictionary;
- (void)setObject:(ObjectType)object forKey:(KeyType)key;
- (ObjectType)objectForKey:(KeyType)key;
@end
@implementation GenericDictionary
- (instancetype)init {
self = [super init];
if (self) {
_dictionary = [NSMutableDictionary dictionary];
}
return self;
}
- (void)setObject:(id)object forKey:(id)key {
self.dictionary[key] = object;
}
- (id)objectForKey:(id)key {
return self.dictionary[key];
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
// Using generic Box
Box<NSString *> *stringBox = [[Box alloc] initWithValue:@"Hello"];
NSLog(@"%@", [stringBox getValue]);
Box<NSNumber *> *numberBox = [[Box alloc] initWithValue:@42];
NSLog(@"%@", [numberBox getValue]);
// Using generic dictionary
GenericDictionary<NSString *, NSNumber *> *dict = [[GenericDictionary alloc] init];
[dict setObject:@25 forKey:@"age"];
[dict setObject:@30 forKey:@"score"];
NSLog(@"%@", [dict objectForKey:@"age"]);
// Array methods
NSArray *array = @[@1, @2, @3];
NSArray *reversed = [ArrayUtils reverseArray:array];
NSLog(@"%@", reversed);
NSLog(@"%@", [ArrayUtils firstElement:array]);
}
return 0;
}Delegation is a design pattern where one object delegates responsibilities to another. It's widely used in iOS/macOS development.
- Delegate protocol: Define protocol
- Delegate property:
@property (nonatomic, weak) id<Protocol> delegate - Responds to selector:
respondsToSelector: - Multiple delegates: Array of delegates
- Common uses: UITableViewDelegate, UITextFieldDelegate
// Delegation Pattern in Objective-C
#import <Foundation/Foundation.h>
// Delegate protocol
@protocol DataSourceDelegate <NSObject>
@optional
- (void)dataDidLoad:(NSArray *)data;
- (void)dataDidFailWithError:(NSError *)error;
@end
// Class that uses delegate
@interface DataSource : NSObject
@property (nonatomic, weak) id<DataSourceDelegate> delegate;
- (void)loadData;
@end
@implementation DataSource
- (void)loadData {
// Simulate async data loading
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[NSThread sleepForTimeInterval:0.5];
dispatch_async(dispatch_get_main_queue(), ^{
if (self.delegate) {
if ([self.delegate respondsToSelector:@selector(dataDidLoad:)]) {
[self.delegate dataDidLoad:@[@"Item 1", @"Item 2", @"Item 3"]];
}
}
});
});
}
@end
// Delegate implementation
@interface ViewController : NSObject <DataSourceDelegate>
- (void)setupDataSource;
@end
@implementation ViewController
- (void)setupDataSource {
DataSource *dataSource = [[DataSource alloc] init];
dataSource.delegate = self;
[dataSource loadData];
}
- (void)dataDidLoad:(NSArray *)data {
NSLog(@"Data loaded: %@", data);
}
- (void)dataDidFailWithError:(NSError *)error {
NSLog(@"Error: %@", error);
}
@end
// Multiple delegates
@protocol MultiDelegate <NSObject>
- (void)handleEvent:(NSString *)event;
@end
@interface EventManager : NSObject
@property (nonatomic, strong) NSMutableArray *delegates;
- (void)addDelegate:(id<MultiDelegate>)delegate;
- (void)triggerEvent;
@end
@implementation EventManager
- (instancetype)init {
self = [super init];
if (self) {
_delegates = [NSMutableArray array];
}
return self;
}
- (void)addDelegate:(id<MultiDelegate>)delegate {
[self.delegates addObject:delegate];
}
- (void)triggerEvent {
for (id<MultiDelegate> delegate in self.delegates) {
if ([delegate respondsToSelector:@selector(handleEvent:)]) {
[delegate handleEvent:@"Event triggered"];
}
}
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
ViewController *vc = [[ViewController alloc] init];
[vc setupDataSource];
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:1.0]];
}
return 0;
}Singleton pattern ensures a class has only one instance. It's implemented using dispatch_once for thread safety.
- sharedInstance: Class method
- dispatch_once: Thread-safe initialization
- Prevent copying: Override
copyWithZone: - Prevent archiving: Override
initWithCoder: - Common uses: AppConfig, DataManager
// Singleton Pattern in Objective-C
#import <Foundation/Foundation.h>
// Singleton class
@interface AppConfig : NSObject
@property (nonatomic, strong) NSString *apiUrl;
@property (nonatomic, assign) NSInteger timeout;
+ (instancetype)sharedInstance;
- (void)printConfig;
@end
@implementation AppConfig
+ (instancetype)sharedInstance {
static AppConfig *sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[self alloc] init];
sharedInstance.apiUrl = @"https://api.example.com";
sharedInstance.timeout = 5000;
});
return sharedInstance;
}
- (void)printConfig {
NSLog(@"API URL: %@", self.apiUrl);
NSLog(@"Timeout: %ld", (long)self.timeout);
}
// Prevent copying
- (id)copyWithZone:(NSZone *)zone {
return self;
}
// Prevent archiving
- (id)initWithCoder:(NSCoder *)coder {
return self;
}
// Prevent archiving
- (void)encodeWithCoder:(NSCoder *)coder {
}
@end
// UserManager singleton with state
@interface UserManager : NSObject
@property (nonatomic, strong) NSMutableArray *users;
+ (instancetype)sharedManager;
- (void)addUser:(NSString *)user;
- (void)removeUser:(NSString *)user;
@end
@implementation UserManager
+ (instancetype)sharedManager {
static UserManager *sharedManager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedManager = [[self alloc] init];
sharedManager.users = [NSMutableArray array];
});
return sharedManager;
}
- (void)addUser:(NSString *)user {
[self.users addObject:user];
}
- (void)removeUser:(NSString *)user {
[self.users removeObject:user];
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
// Using singleton
AppConfig *config1 = [AppConfig sharedInstance];
AppConfig *config2 = [AppConfig sharedInstance];
NSLog(@"%d", config1 == config2); // 1 (true)
[config1 printConfig];
// UserManager
UserManager *manager = [UserManager sharedManager];
[manager addUser:@"Alice"];
[manager addUser:@"Bob"];
NSLog(@"Users: %@", manager.users);
UserManager *manager2 = [UserManager sharedManager];
[manager2 addUser:@"Charlie"];
NSLog(@"Users: %@", manager.users);
}
return 0;
}Factory pattern is implemented using class methods that create and return instances of different classes.
- Factory method:
+ (instancetype)createWithType:(NSString *)type - Object creation: Based on parameters
- Abstract factory: Factory of factories
- Common uses: Creating different object types
- Benefits: Decoupling creation logic
// Factory Pattern in Objective-C
#import <Foundation/Foundation.h>
// Base classes
@interface User : NSObject
@property (nonatomic, strong) NSString *name;
- (NSString *)getRole;
@end
@implementation User
- (NSString *)getRole { return @"user"; }
@end
@interface Admin : User
@end
@implementation Admin
- (NSString *)getRole { return @"admin"; }
@end
@interface Guest : User
@end
@implementation Guest
- (NSString *)getRole { return @"guest"; }
@end
// Factory class
@interface UserFactory : NSObject
+ (User *)createUserWithType:(NSString *)type name:(NSString *)name;
@end
@implementation UserFactory
+ (User *)createUserWithType:(NSString *)type name:(NSString *)name {
User *user = nil;
if ([type isEqualToString:@"admin"]) {
user = [[Admin alloc] init];
} else if ([type isEqualToString:@"guest"]) {
user = [[Guest alloc] init];
} else {
user = [[User alloc] init];
}
user.name = name;
return user;
}
@end
// Abstract factory
@protocol Widget <NSObject>
- (void)draw;
@end
@interface Button : NSObject <Widget>
@end
@implementation Button
- (void)draw { NSLog(@"Drawing Button"); }
@end
@interface TextField : NSObject <Widget>
@end
@implementation TextField
- (void)draw { NSLog(@"Drawing TextField"); }
@end
@interface WidgetFactory : NSObject
+ (id<Widget>)createWidget:(NSString *)type;
@end
@implementation WidgetFactory
+ (id<Widget>)createWidget:(NSString *)type {
if ([type isEqualToString:@"button"]) {
return [[Button alloc] init];
} else if ([type isEqualToString:@"textfield"]) {
return [[TextField alloc] init];
}
return nil;
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
User *admin = [UserFactory createUserWithType:@"admin" name:@"Alice"];
User *guest = [UserFactory createUserWithType:@"guest" name:@"Bob"];
NSLog(@"%@ role: %@", admin.name, [admin getRole]);
NSLog(@"%@ role: %@", guest.name, [guest getRole]);
id<Widget> button = [WidgetFactory createWidget:@"button"];
id<Widget> textField = [WidgetFactory createWidget:@"textfield"];
[button draw];
[textField draw];
}
return 0;
}Strategy pattern defines a family of algorithms and makes them interchangeable. It's implemented using protocols and composition.
- Strategy protocol: Define algorithm interface
- Concrete strategies: Implement protocol
- Context class: Uses strategy
- Runtime switching: Change strategy at runtime
- Benefits: Encapsulate algorithms
// Strategy Pattern in Objective-C
#import <Foundation/Foundation.h>
// Strategy protocol
@protocol PaymentStrategy <NSObject>
- (void)pay:(double)amount;
@end
// Concrete strategies
@interface CreditCardStrategy : NSObject <PaymentStrategy>
@end
@implementation CreditCardStrategy
- (void)pay:(double)amount {
NSLog(@"Paid $%.2f with Credit Card", amount);
}
@end
@interface PayPalStrategy : NSObject <PaymentStrategy>
@end
@implementation PayPalStrategy
- (void)pay:(double)amount {
NSLog(@"Paid $%.2f with PayPal", amount);
}
@end
@interface CryptoStrategy : NSObject <PaymentStrategy>
@end
@implementation CryptoStrategy
- (void)pay:(double)amount {
NSLog(@"Paid $%.2f with Crypto", amount);
}
@end
// Context class
@interface PaymentContext : NSObject
@property (nonatomic, strong) id<PaymentStrategy> strategy;
- (instancetype)initWithStrategy:(id<PaymentStrategy>)strategy;
- (void)executePayment:(double)amount;
@end
@implementation PaymentContext
- (instancetype)initWithStrategy:(id<PaymentStrategy>)strategy {
self = [super init];
if (self) {
_strategy = strategy;
}
return self;
}
- (void)executePayment:(double)amount {
[self.strategy pay:amount];
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
PaymentContext *context;
context = [[PaymentContext alloc] initWithStrategy:[[CreditCardStrategy alloc] init]];
[context executePayment:100.0];
context.strategy = [[PayPalStrategy alloc] init];
[context executePayment:50.0];
context.strategy = [[CryptoStrategy alloc] init];
[context executePayment:75.0];
}
return 0;
}The observer pattern in Objective-C is implemented using NSNotificationCenter, KVO, or custom delegate protocols.
- NSNotificationCenter: Centralized notification system
- KVO: Key-Value Observing for property changes
- Delegates: One-to-one communication
- Custom observers: Protocol-based observer pattern
- Benefits: Loose coupling, event-driven architecture
// Observer Pattern in Objective-C
#import <Foundation/Foundation.h>
// Observer protocol
@protocol Observer <NSObject>
- (void)update:(NSString *)data;
@end
// Subject class
@interface Subject : NSObject
@property (nonatomic, strong) NSString *state;
@property (nonatomic, strong) NSMutableArray *observers;
- (void)attach:(id<Observer>)observer;
- (void)detach:(id<Observer>)observer;
- (void)setState:(NSString *)state;
@end
@implementation Subject
- (instancetype)init {
self = [super init];
if (self) {
_observers = [NSMutableArray array];
_state = @"";
}
return self;
}
- (void)attach:(id<Observer>)observer {
[self.observers addObject:observer];
}
- (void)detach:(id<Observer>)observer {
[self.observers removeObject:observer];
}
- (void)setState:(NSString *)state {
_state = state;
[self notifyObservers];
}
- (void)notifyObservers {
for (id<Observer> observer in self.observers) {
[observer update:self.state];
}
}
@end
// Concrete observer
@interface ConcreteObserver : NSObject <Observer>
@property (nonatomic, strong) NSString *name;
- (instancetype)initWithName:(NSString *)name;
@end
@implementation ConcreteObserver
- (instancetype)initWithName:(NSString *)name {
self = [super init];
if (self) {
_name = name;
}
return self;
}
- (void)update:(NSString *)data {
NSLog(@"%@ received: %@", self.name, data);
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
Subject *subject = [[Subject alloc] init];
ConcreteObserver *observer1 = [[ConcreteObserver alloc] initWithName:@"Observer1"];
ConcreteObserver *observer2 = [[ConcreteObserver alloc] initWithName:@"Observer2"];
[subject attach:observer1];
[subject attach:observer2];
[subject setState:@"Hello World"];
[subject detach:observer1];
[subject setState:@"Hello again"];
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.5]];
}
return 0;
}Reverse a string using reversed or manual iteration.
- Built-in:
[[str reverseObjectEnumerator] allObjects] - Manual: Iterate from end to start
- NSMutableString: Use
replaceCharactersInRange - Complexity: O(n) time
// Reverse a string in Objective-C
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSString *str = @"hello";
// Using built-in method
NSMutableString *reversed = [NSMutableString string];
for (NSInteger i = [str length] - 1; i >= 0; i--) {
[reversed appendFormat:@"%C", [str characterAtIndex:i]];
}
NSLog(@"%@", reversed); // "olleh"
// Using reverseObjectEnumerator
NSArray *chars = [str componentsSeparatedByString:@""];
NSArray *reversedChars = [[chars reverseObjectEnumerator] allObjects];
NSString *reversed2 = [reversedChars componentsJoinedByString:@""];
NSLog(@"%@", reversed2); // "olleh"
// Manual implementation
NSString *reverseString(NSString *str) {
NSMutableString *result = [NSMutableString stringWithCapacity:str.length];
for (NSInteger i = str.length - 1; i >= 0; i--) {
[result appendFormat:@"%C", [str characterAtIndex:i]];
}
return result;
}
NSLog(@"%@", reverseString(str));
}
return 0;
}Check if a string is a palindrome using two-pointer approach or reverse comparison.
- Two-pointer: Compare from both ends
- Reverse comparison: Compare with reversed string
- Case insensitive: Use
lowercaseString - Ignoring non-alphanumeric: Use
NSCharacterSet
// Check palindrome in Objective-C
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSString *str = @"racecar";
// Using reverse comparison
BOOL isPalindrome(NSString *s) {
NSMutableString *reversed = [NSMutableString string];
for (NSInteger i = [s length] - 1; i >= 0; i--) {
[reversed appendFormat:@"%C", [s characterAtIndex:i]];
}
return [s isEqualToString:reversed];
}
NSLog(@"%d", isPalindrome(str)); // 1 (true)
// Two-pointer approach
BOOL isPalindromeTwoPointer(NSString *s) {
NSInteger left = 0;
NSInteger right = [s length] - 1;
while (left < right) {
if ([s characterAtIndex:left] != [s characterAtIndex:right]) {
return NO;
}
left++;
right--;
}
return YES;
}
NSLog(@"%d", isPalindromeTwoPointer(str)); // 1 (true)
// Case insensitive
BOOL isPalindromeCaseInsensitive(NSString *s) {
NSString *lower = [s lowercaseString];
NSMutableString *cleaned = [NSMutableString string];
for (NSInteger i = 0; i < lower.length; i++) {
unichar c = [lower characterAtIndex:i];
if ([[NSCharacterSet alphanumericCharacterSet] characterIsMember:c]) {
[cleaned appendFormat:@"%C", c];
}
}
return [cleaned isEqualToString:[[cleaned reverseObjectEnumerator] allObjects]];
}
NSLog(@"%d", isPalindromeCaseInsensitive(@"A man a plan a canal Panama")); // 1
}
return 0;
}Find maximum value using @max operator or manual iteration.
- KVC:
[array valueForKeyPath:@"@max.self"] - Manual: Iterate and track max
- Empty array: Return nil
- Complexity: O(n) time
// Find max in array in Objective-C
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSArray *numbers = @[@1, @5, @3, @9, @2];
// Using valueForKeyPath
NSNumber *max = [numbers valueForKeyPath:@"@max.self"];
NSLog(@"%@", max); // 9
// Manual implementation
NSNumber *findMax(NSArray *arr) {
if (arr.count == 0) return nil;
NSNumber *maxVal = arr[0];
for (NSNumber *num in arr) {
if ([num compare:maxVal] == NSOrderedDescending) {
maxVal = num;
}
}
return maxVal;
}
NSLog(@"%@", findMax(numbers));
}
return 0;
}Remove duplicates using NSSet or NSOrderedSet for order preservation.
- NSSet:
[[NSSet setWithArray:array] allObjects] - NSOrderedSet: Preserves order
- Manual: Track seen objects
- Complexity: O(n) time
// Remove duplicates in Objective-C
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSArray *arr = @[@1, @2, @2, @3, @3, @4];
// Using NSSet
NSArray *unique = [[NSSet setWithArray:arr] allObjects];
NSLog(@"%@", unique); // [1, 2, 3, 4] (order not preserved)
// Preserving order
NSArray *removeDuplicates(NSArray *array) {
NSMutableArray *result = [NSMutableArray array];
NSMutableSet *seen = [NSMutableSet set];
for (id obj in array) {
if (![seen containsObject:obj]) {
[seen addObject:obj];
[result addObject:obj];
}
}
return result;
}
NSLog(@"%@", removeDuplicates(arr));
// Using NSOrderedSet
NSArray *uniqueOrdered = [[NSOrderedSet orderedSetWithArray:arr] array];
NSLog(@"%@", uniqueOrdered);
}
return 0;
}Merge arrays using arrayByAddingObjectsFromArray or NSMutableArray.
- NSArray:
[array1 arrayByAddingObjectsFromArray:array2] - NSMutableArray:
[mutableArray addObjectsFromArray:array2] - Unique merge: Use NSSet
- Complexity: O(n) time
// Merge arrays in Objective-C
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSArray *arr1 = @[@1, @2];
NSArray *arr2 = @[@3, @4];
// Using arrayByAddingObjectsFromArray
NSArray *merged = [arr1 arrayByAddingObjectsFromArray:arr2];
NSLog(@"%@", merged); // [1, 2, 3, 4]
// Using mutable array
NSMutableArray *mutableMerged = [NSMutableArray arrayWithArray:arr1];
[mutableMerged addObjectsFromArray:arr2];
NSLog(@"%@", mutableMerged);
// Merge and remove duplicates
NSArray *mergeUnique(NSArray *a, NSArray *b) {
NSMutableSet *set = [NSMutableSet setWithArray:a];
[set addObjectsFromArray:b];
return [set allObjects];
}
NSLog(@"%@", mergeUnique(@[@1, @2, @3], @[@3, @4, @5]));
}
return 0;
}Convert string to number using integerValue, doubleValue, or NSNumberFormatter.
- integerValue:
[str integerValue] - doubleValue:
[str doubleValue] - NSNumberFormatter: Safe conversion
- NSScanner: Parse with validation
// Convert string to number in Objective-C
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSString *str = @"42";
// Using integerValue
NSInteger intVal = [str integerValue];
NSLog(@"%ld", (long)intVal); // 42
// Using doubleValue
double doubleVal = [str doubleValue];
NSLog(@"%f", doubleVal); // 42.0
// Using NSNumberFormatter (safe)
NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
NSNumber *num = [formatter numberFromString:str];
if (num) {
NSLog(@"%@", num); // 42
}
// Using scan methods
int scanVal;
NSScanner *scanner = [NSScanner scannerWithString:str];
if ([scanner scanInt:&scanVal]) {
NSLog(@"%d", scanVal); // 42
}
// With error handling
NSInteger stringToNumber(NSString *s) {
NSInteger result = [s integerValue];
if (result == 0 && ![s isEqualToString:@"0"]) {
// Invalid number
return NSNotFound;
}
return result;
}
NSLog(@"%ld", (long)stringToNumber(@"42"));
NSLog(@"%ld", (long)stringToNumber(@"invalid"));
}
return 0;
}Iterate through dictionary using fast enumeration or block enumeration.
- Fast enumeration:
for (id key in dict) - Block enumeration:
[dict enumerateKeysAndObjectsUsingBlock:] - Key enumerator:
[dict keyEnumerator] - Break early: Set
*stop = YES
// Loop through dictionary in Objective-C
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSDictionary *dict = @{@"name": @"Alice", @"age": @25, @"city": @"NYC"};
// Using fast enumeration
for (NSString *key in dict) {
NSLog(@"%@ => %@", key, dict[key]);
}
// Using enumerateKeysAndObjectsUsingBlock
[dict enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
NSLog(@"%@ => %@", key, obj);
}];
// Using allKeys
for (NSString *key in [dict allKeys]) {
NSLog(@"%@ => %@", key, [dict objectForKey:key]);
}
// Using keyEnumerator
NSEnumerator *enumerator = [dict keyEnumerator];
NSString *key;
while ((key = [enumerator nextObject])) {
NSLog(@"%@ => %@", key, dict[key]);
}
// Loop with break
[dict enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
if ([key isEqualToString:@"age"]) {
*stop = YES;
}
NSLog(@"%@ => %@", key, obj);
}];
}
return 0;
}Delay execution using GCD, NSTimer, or performSelector.
- performSelector:
performSelector:withObject:afterDelay: - GCD:
dispatch_after - NSTimer:
scheduledTimerWithTimeInterval - NSThread:
sleepForTimeInterval
// Delay function execution in Objective-C
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
@autoreleasepool {
// Using performSelector with afterDelay
[self performSelector:@selector(delayedMethod) withObject:nil afterDelay:2.0];
// Using GCD
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)),
dispatch_get_main_queue(), ^{
NSLog(@"After 2 seconds (GCD)");
});
// Using NSTimer
[NSTimer scheduledTimerWithTimeInterval:2.0
target:self
selector:@selector(timerFired:)
userInfo:nil
repeats:NO];
// Using NSThread
[NSThread sleepForTimeInterval:2.0];
NSLog(@"After 2 seconds (NSThread)");
// Custom delay function
void delay(double seconds, void (^block)(void)) {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(seconds * NSEC_PER_SEC)),
dispatch_get_main_queue(), block);
}
delay(2.0, ^{
NSLog(@"After 2 seconds (custom)");
});
// Keep main thread alive
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:3.0]];
}
return 0;
}
- (void)delayedMethod {
NSLog(@"After 2 seconds (performSelector)");
}
- (void)timerFired:(NSTimer *)timer {
NSLog(@"After 2 seconds (NSTimer)");
}Make HTTP GET requests using NSURLSession or NSURLConnection.
- NSURLSession:
dataTaskWithURL:completionHandler: - NSURLConnection:
sendSynchronousRequest - Headers:
setValue:forHTTPHeaderField: - Error handling: Check NSError
// HTTP GET request in Objective-C
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
@autoreleasepool {
// Using NSURLSession
NSURL *url = [NSURL URLWithString:@"https://api.example.com/data"];
NSURLSessionDataTask *task = [[NSURLSession sharedSession]
dataTaskWithURL:url
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
if (error) {
NSLog(@"Error: %@", error);
return;
}
NSString *result = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(@"Response: %@", result);
}];
[task resume];
// Synchronous request (blocking)
NSURL *url2 = [NSURL URLWithString:@"https://api.example.com/data"];
NSURLRequest *request = [NSURLRequest requestWithURL:url2];
NSURLResponse *response = nil;
NSError *error = nil;
NSData *data = [NSURLConnection sendSynchronousRequest:request
returningResponse:&response
error:&error];
if (error) {
NSLog(@"Error: %@", error);
} else {
NSString *result = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(@"Response: %@", result);
}
// With headers
NSMutableURLRequest *request2 = [NSMutableURLRequest requestWithURL:url];
[request2 setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
[request2 setValue:@"Bearer token" forHTTPHeaderField:@"Authorization"];
NSURLSessionDataTask *task2 = [[NSURLSession sharedSession]
dataTaskWithRequest:request2
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
// Handle response
}];
[task2 resume];
// Keep main thread alive
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:3.0]];
}
return 0;
}Create a Deferred using blocks with resolve and reject callbacks.
- Deferred class: Custom class with completion
- Async block: Execute asynchronously
- then method: Handle result or error
- Polling: Wait for completion
// Create a promise-like Deferred in Objective-C
#import <Foundation/Foundation.h>
// Deferred class
@interface Deferred : NSObject
@property (nonatomic, strong) id result;
@property (nonatomic, strong) NSError *error;
@property (nonatomic, assign) BOOL completed;
- (instancetype)initWithBlock:(void (^)(void (^resolve)(id), void (^reject)(NSError *)))block;
- (void)then:(void (^)(id))onFulfilled onRejected:(void (^)(NSError *))onRejected;
@end
@implementation Deferred
- (instancetype)initWithBlock:(void (^)(void (^resolve)(id), void (^reject)(NSError *)))block {
self = [super init];
if (self) {
_completed = NO;
__weak typeof(self) weakSelf = self;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
block(^(id result) {
weakSelf.result = result;
weakSelf.completed = YES;
}, ^(NSError *error) {
weakSelf.error = error;
weakSelf.completed = YES;
});
});
}
return self;
}
- (void)then:(void (^)(id))onFulfilled onRejected:(void (^)(NSError *))onRejected {
__weak typeof(self) weakSelf = self;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
while (!weakSelf.completed) {
[NSThread sleepForTimeInterval:0.01];
}
dispatch_async(dispatch_get_main_queue(), ^{
if (weakSelf.error) {
onRejected(weakSelf.error);
} else {
onFulfilled(weakSelf.result);
}
});
});
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
// Create deferred
Deferred *deferred = [[Deferred alloc] initWithBlock:^(void (^resolve)(id), void (^reject)(NSError *)) {
[NSThread sleepForTimeInterval:1.0];
resolve(@"Success!");
}];
// Handle result
[deferred then:^(id result) {
NSLog(@"Result: %@", result);
} onRejected:^(NSError *error) {
NSLog(@"Error: %@", error);
}];
// Keep main thread alive
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:2.0]];
}
return 0;
}Calculate factorial using recursion or iteration.
- Recursive:
n * factorial(n-1) - Iterative: Loop with multiplication
- Base case:
n <= 1 - Edge cases: 0! = 1
// Factorial in Objective-C
#import <Foundation/Foundation.h>
// Recursive factorial
NSInteger factorial(NSInteger n) {
if (n <= 1) return 1;
return n * factorial(n - 1);
}
// Iterative factorial
NSInteger factorialIterative(NSInteger n) {
NSInteger result = 1;
for (NSInteger i = 2; i <= n; i++) {
result *= i;
}
return result;
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSLog(@"%ld", (long)factorial(5)); // 120
NSLog(@"%ld", (long)factorialIterative(5)); // 120
}
return 0;
}Calculate Fibonacci using recursion, iteration, or memoization.
- Recursive:
fib(n-1) + fib(n-2) - Iterative: Loop with variables
- Memoization: Cache results in dictionary
- Complexity: O(n) with memoization
// Fibonacci in Objective-C
#import <Foundation/Foundation.h>
// Recursive Fibonacci
NSInteger fibonacci(NSInteger n) {
if (n <= 1) return n;
return fibonacci(n - 1) + fibonacci(n - 2);
}
// Iterative Fibonacci
NSInteger fibonacciIterative(NSInteger n) {
if (n <= 1) return n;
NSInteger a = 0, b = 1;
for (NSInteger i = 2; i <= n; i++) {
NSInteger temp = a + b;
a = b;
b = temp;
}
return b;
}
// Memoized Fibonacci
NSInteger fibonacciMemo(NSInteger n, NSMutableDictionary *memo) {
if (n <= 1) return n;
NSNumber *cached = memo[@(n)];
if (cached) return [cached integerValue];
NSInteger result = fibonacciMemo(n - 1, memo) + fibonacciMemo(n - 2, memo);
memo[@(n)] = @(result);
return result;
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSLog(@"%ld", (long)fibonacci(8)); // 21
NSLog(@"%ld", (long)fibonacciIterative(8)); // 21
NSLog(@"%ld", (long)fibonacciMemo(8, [NSMutableDictionary dictionary])); // 21
}
return 0;
}FizzBuzz using if-else or switch statement.
- Modulo:
i % 15 == 0 - Order: Check 15 first
- Range:
for (NSInteger i = 1; i <= n; i++) - Return array: Collect results
// FizzBuzz in Objective-C
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
@autoreleasepool {
for (NSInteger i = 1; i <= 15; i++) {
if (i % 15 == 0) {
NSLog(@"FizzBuzz");
} else if (i % 3 == 0) {
NSLog(@"Fizz");
} else if (i % 5 == 0) {
NSLog(@"Buzz");
} else {
NSLog(@"%ld", (long)i);
}
}
// Return as array
NSArray *fizzbuzzArray(NSInteger n) {
NSMutableArray *result = [NSMutableArray array];
for (NSInteger i = 1; i <= n; i++) {
if (i % 15 == 0) {
[result addObject:@"FizzBuzz"];
} else if (i % 3 == 0) {
[result addObject:@"Fizz"];
} else if (i % 5 == 0) {
[result addObject:@"Buzz"];
} else {
[result addObject:[NSString stringWithFormat:@"%ld", (long)i]];
}
}
return result;
}
NSLog(@"%@", fizzbuzzArray(15));
}
return 0;
}Find missing number using formula or XOR method.
- Formula:
total - sum - XOR: XOR all numbers and indices
- Complexity: O(n) time
- Edge cases: Empty array, missing first or last
// Find missing number in Objective-C
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSArray *arr = @[@1, @2, @4, @5, @6];
// Using formula
NSInteger findMissing(NSArray *array) {
NSInteger n = array.count + 1;
NSInteger total = n * (n + 1) / 2;
NSInteger sum = [[array valueForKeyPath:@"@sum.self"] integerValue];
return total - sum;
}
NSLog(@"%ld", (long)findMissing(arr)); // 3
// Using XOR
NSInteger findMissingXOR(NSArray *array) {
NSInteger n = array.count + 1;
NSInteger xorSum = 0;
for (NSInteger i = 1; i <= n; i++) {
xorSum ^= i;
}
for (NSNumber *num in array) {
xorSum ^= [num integerValue];
}
return xorSum;
}
NSLog(@"%ld", (long)findMissingXOR(arr)); // 3
}
return 0;
}Find duplicates using NSSet or NSCountedSet.
- NSSet: Track seen elements
- NSCountedSet: Count occurrences
- Return: Elements with count > 1
- Complexity: O(n) time
// Find duplicates in Objective-C
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSArray *arr = @[@1, @2, @3, @2, @4, @3];
// Using NSSet
NSArray *findDuplicates(NSArray *array) {
NSMutableSet *seen = [NSMutableSet set];
NSMutableSet *duplicates = [NSMutableSet set];
for (id obj in array) {
if ([seen containsObject:obj]) {
[duplicates addObject:obj];
} else {
[seen addObject:obj];
}
}
return [duplicates allObjects];
}
NSLog(@"%@", findDuplicates(arr)); // [2, 3]
// Using NSCountedSet
NSArray *findDuplicatesCounted(NSArray *array) {
NSCountedSet *counted = [NSCountedSet setWithArray:array];
NSMutableArray *duplicates = [NSMutableArray array];
for (id obj in counted) {
if ([counted countForObject:obj] > 1) {
[duplicates addObject:obj];
}
}
return duplicates;
}
NSLog(@"%@", findDuplicatesCounted(arr));
}
return 0;
}Calculate sum using @sum operator or manual iteration.
- KVC:
[array valueForKeyPath:@"@sum.self"] - Manual: Iterate and accumulate
- Empty array: Returns 0
- Complexity: O(n) time
// Sum of array in Objective-C
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSArray *arr = @[@1, @2, @3, @4, @5];
// Using valueForKeyPath
NSNumber *sum = [arr valueForKeyPath:@"@sum.self"];
NSLog(@"%@", sum); // 15
// Manual implementation
NSInteger sumArray(NSArray *array) {
NSInteger total = 0;
for (NSNumber *num in array) {
total += [num integerValue];
}
return total;
}
NSLog(@"%ld", (long)sumArray(arr)); // 15
// Using reduce (through block)
NSNumber *sumReduce(NSArray *array) {
return [array valueForKeyPath:@"@sum.self"];
}
NSLog(@"%@", sumReduce(arr));
}
return 0;
}Calculate average using @avg operator or manual division.
- KVC:
[array valueForKeyPath:@"@avg.self"] - Manual:
sum / count - Empty array: Returns 0
- Precision: Returns double
// Average of array in Objective-C
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSArray *arr = @[@1, @2, @3, @4, @5];
// Using valueForKeyPath
NSNumber *avg = [arr valueForKeyPath:@"@avg.self"];
NSLog(@"%@", avg); // 3.0
// Manual implementation
double averageArray(NSArray *array) {
if (array.count == 0) return 0;
NSInteger total = 0;
for (NSNumber *num in array) {
total += [num integerValue];
}
return (double)total / array.count;
}
NSLog(@"%f", averageArray(arr)); // 3.0
}
return 0;
}Sort using sortedArrayUsingSelector or sortedArrayUsingComparator.
- Selector:
sortedArrayUsingSelector:@selector(compare:) - Comparator:
sortedArrayUsingComparator:^NSComparisonResult - Sort descriptors:
sortedArrayUsingDescriptors: - In-place:
sortUsingSelector:
// Sort array ascending in Objective-C
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSArray *arr = @[@5, @2, @8, @1, @9];
// Using sortedArrayUsingSelector
NSArray *sorted = [arr sortedArrayUsingSelector:@selector(compare:)];
NSLog(@"%@", sorted); // [1, 2, 5, 8, 9]
// Using sortedArrayUsingComparator
NSArray *sorted2 = [arr sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) {
return [obj1 compare:obj2];
}];
NSLog(@"%@", sorted2);
// Using sort descriptors
NSSortDescriptor *descriptor = [NSSortDescriptor sortDescriptorWithKey:@"self" ascending:YES];
NSArray *sorted3 = [arr sortedArrayUsingDescriptors:@[descriptor]];
NSLog(@"%@", sorted3);
// In-place sorting (NSMutableArray)
NSMutableArray *mutableArr = [NSMutableArray arrayWithArray:arr];
[mutableArr sortUsingSelector:@selector(compare:)];
NSLog(@"%@", mutableArr);
}
return 0;
}Sort descending by reversing comparison or using ascending:NO.
- Comparator:
return [obj2 compare:obj1] - Sort descriptors:
ascending:NO - In-place:
sortUsingComparator: - Complexity: O(n log n)
// Sort array descending in Objective-C
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSArray *arr = @[@5, @2, @8, @1, @9];
// Using sortedArrayUsingSelector with NSOrderedDescending
NSArray *sorted = [arr sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) {
return [obj2 compare:obj1]; // Reverse order
}];
NSLog(@"%@", sorted); // [9, 8, 5, 2, 1]
// Using sort descriptors
NSSortDescriptor *descriptor = [NSSortDescriptor sortDescriptorWithKey:@"self" ascending:NO];
NSArray *sorted2 = [arr sortedArrayUsingDescriptors:@[descriptor]];
NSLog(@"%@", sorted2);
// In-place sorting (NSMutableArray)
NSMutableArray *mutableArr = [NSMutableArray arrayWithArray:arr];
[mutableArr sortUsingComparator:^NSComparisonResult(id obj1, id obj2) {
return [obj2 compare:obj1];
}];
NSLog(@"%@", mutableArr);
}
return 0;
}Flatten nested arrays using recursion or iterative approach.
- Recursive: Check if element is array
- NSArray:
addObjectsFromArray - Type checking:
isKindOfClass:[NSArray class] - Complexity: O(n) time
// Flatten nested array in Objective-C
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSArray *nested = @[@1, @[@2, @[@3, @4], @5], @6];
// Recursive flatten
NSArray *flattenArray(NSArray *array) {
NSMutableArray *result = [NSMutableArray array];
for (id obj in array) {
if ([obj isKindOfClass:[NSArray class]]) {
[result addObjectsFromArray:flattenArray(obj)];
} else {
[result addObject:obj];
}
}
return result;
}
NSLog(@"%@", flattenArray(nested)); // [1, 2, 3, 4, 5, 6]
// Using blocks (for 2D only)
NSArray *flatten2D(NSArray *array) {
NSMutableArray *result = [NSMutableArray array];
for (id obj in array) {
if ([obj isKindOfClass:[NSArray class]]) {
[result addObjectsFromArray:obj];
} else {
[result addObject:obj];
}
}
return result;
}
NSArray *nested2D = @[@[@1, @2], @[@3, @4], @[@5, @6]];
NSLog(@"%@", flatten2D(nested2D));
}
return 0;
}Split array into chunks using subarrayWithRange.
- Loop: Iterate with step size
- subarrayWithRange: Extract chunks
- Edge case: Handle last chunk
- Complexity: O(n) time
// Chunk array in Objective-C
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSArray *arr = @[@1, @2, @3, @4, @5, @6];
NSArray *chunkArray(NSArray *array, NSInteger size) {
NSMutableArray *result = [NSMutableArray array];
for (NSInteger i = 0; i < array.count; i += size) {
NSInteger end = MIN(i + size, array.count);
NSArray *chunk = [array subarrayWithRange:NSMakeRange(i, end - i)];
[result addObject:chunk];
}
return result;
}
NSLog(@"%@", chunkArray(arr, 2)); // [[1, 2], [3, 4], [5, 6]]
// Using enumerateObjectsUsingBlock
NSArray *chunkArrayBlock(NSArray *array, NSInteger size) {
NSMutableArray *result = [NSMutableArray array];
__block NSMutableArray *currentChunk = [NSMutableArray array];
[array enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
[currentChunk addObject:obj];
if (currentChunk.count == size || idx == array.count - 1) {
[result addObject:currentChunk];
currentChunk = [NSMutableArray array];
}
}];
return result;
}
NSLog(@"%@", chunkArrayBlock(arr, 2));
}
return 0;
}Binary search using while loop or indexOfObject:inSortedRange:options:.
- Manual: While loop with left/right pointers
- Foundation:
indexOfObject:inSortedRange:options: - Requirement: Array must be sorted
- Complexity: O(log n) time
// Binary search in Objective-C
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSArray *arr = @[@1, @2, @3, @4, @5, @6, @7];
NSInteger binarySearch(NSArray *array, NSInteger target) {
NSInteger left = 0;
NSInteger right = array.count - 1;
while (left <= right) {
NSInteger mid = (left + right) / 2;
NSInteger midVal = [array[mid] integerValue];
if (midVal == target) {
return mid;
} else if (midVal < target) {
left = mid + 1;
} else {
right = mid - 1;
}
}
return -1;
}
NSLog(@"%ld", (long)binarySearch(arr, 5)); // 4
// Using indexOfObject (linear search)
NSInteger index = [arr indexOfObject:@5];
NSLog(@"%ld", (long)index); // 4
// Using binary search with NSArray (requires sorted array)
NSInteger binarySearchFoundation(NSArray *array, id target) {
return [array indexOfObject:target
inSortedRange:NSMakeRange(0, array.count)
options:NSBinarySearchingFirstEqual
usingComparator:^NSComparisonResult(id obj1, id obj2) {
return [obj1 compare:obj2];
}];
}
NSLog(@"%ld", (long)binarySearchFoundation(arr, @5));
}
return 0;
}Quick sort using recursion and partitioning.
- Algorithm: Choose pivot, partition, recurse
- Time: O(n log n) average
- In-place: Implement for performance
- Pivot: First element or random
// Quick sort in Objective-C
#import <Foundation/Foundation.h>
NSArray *quickSort(NSArray *array) {
if (array.count <= 1) return array;
NSInteger pivot = [array[0] integerValue];
NSMutableArray *left = [NSMutableArray array];
NSMutableArray *right = [NSMutableArray array];
for (NSInteger i = 1; i < array.count; i++) {
NSInteger val = [array[i] integerValue];
if (val < pivot) {
[left addObject:array[i]];
} else {
[right addObject:array[i]];
}
}
NSMutableArray *result = [NSMutableArray array];
[result addObjectsFromArray:quickSort(left)];
[result addObject:@(pivot)];
[result addObjectsFromArray:quickSort(right)];
return result;
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSArray *arr = @[@5, @3, @8, @4, @2, @7, @1, @6];
NSArray *sorted = quickSort(arr);
NSLog(@"%@", sorted);
}
return 0;
}Merge sort using divide-and-conquer and merging.
- Algorithm: Divide, sort, merge
- Time: O(n log n)
- Stable: Maintains relative order
- Space: O(n) auxiliary space
// Merge sort in Objective-C
#import <Foundation/Foundation.h>
NSArray *mergeSort(NSArray *array) {
if (array.count <= 1) return array;
NSInteger mid = array.count / 2;
NSArray *left = [array subarrayWithRange:NSMakeRange(0, mid)];
NSArray *right = [array subarrayWithRange:NSMakeRange(mid, array.count - mid)];
return merge(mergeSort(left), mergeSort(right));
}
NSArray *merge(NSArray *left, NSArray *right) {
NSMutableArray *result = [NSMutableArray array];
NSInteger i = 0, j = 0;
while (i < left.count && j < right.count) {
if ([left[i] integerValue] <= [right[j] integerValue]) {
[result addObject:left[i++]];
} else {
[result addObject:right[j++]];
}
}
while (i < left.count) {
[result addObject:left[i++]];
}
while (j < right.count) {
[result addObject:right[j++]];
}
return result;
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSArray *arr = @[@5, @3, @8, @4, @2, @7, @1, @6];
NSArray *sorted = mergeSort(arr);
NSLog(@"%@", sorted);
}
return 0;
}Bubble sort with early termination optimization.
- Algorithm: Compare adjacent, swap
- Time: O(n²) worst case
- Optimization: Stop if no swaps
- In-place: Modifies original array
// Bubble sort in Objective-C
#import <Foundation/Foundation.h>
NSArray *bubbleSort(NSArray *array) {
NSMutableArray *sorted = [NSMutableArray arrayWithArray:array];
for (NSInteger i = 0; i < sorted.count - 1; i++) {
for (NSInteger j = 0; j < sorted.count - 1 - i; j++) {
if ([sorted[j] integerValue] > [sorted[j+1] integerValue]) {
[sorted exchangeObjectAtIndex:j withObjectAtIndex:j+1];
}
}
}
return sorted;
}
NSArray *bubbleSortOptimized(NSArray *array) {
NSMutableArray *sorted = [NSMutableArray arrayWithArray:array];
BOOL swapped;
for (NSInteger i = 0; i < sorted.count - 1; i++) {
swapped = NO;
for (NSInteger j = 0; j < sorted.count - 1 - i; j++) {
if ([sorted[j] integerValue] > [sorted[j+1] integerValue]) {
[sorted exchangeObjectAtIndex:j withObjectAtIndex:j+1];
swapped = YES;
}
}
if (!swapped) break;
}
return sorted;
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSArray *arr = @[@5, @3, @8, @4, @2, @7, @1, @6];
NSLog(@"%@", bubbleSort(arr));
NSLog(@"%@", bubbleSortOptimized(arr));
}
return 0;
}Find common elements using NSSet or predicate.
- NSSet:
[setA intersectsSet:setB] - Predicate:
filteredArrayUsingPredicate: - Manual: Check if in set
- Complexity: O(n) time with set
// Intersection of arrays in Objective-C
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSArray *arr1 = @[@1, @2, @3, @4];
NSArray *arr2 = @[@3, @4, @5, @6];
// Using NSSet
NSArray *intersection = [[NSSet setWithArray:arr1] intersectsSet:[NSSet setWithArray:arr2]];
NSLog(@"%d", intersection);
// Manual intersection
NSArray *intersectionManual(NSArray *a, NSArray *b) {
NSMutableSet *setB = [NSMutableSet setWithArray:b];
NSMutableArray *result = [NSMutableArray array];
for (id obj in a) {
if ([setB containsObject:obj]) {
[result addObject:obj];
}
}
return result;
}
NSLog(@"%@", intersectionManual(arr1, arr2)); // [3, 4]
// Using predicate
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF IN %@", arr2];
NSArray *intersectionPredicate = [arr1 filteredArrayUsingPredicate:predicate];
NSLog(@"%@", intersectionPredicate);
}
return 0;
}Combine arrays with unique elements using NSSet or NSOrderedSet.
- NSSet:
[setA unionSet:setB] - NSOrderedSet: Preserves order
- Manual: Add unique elements
- Complexity: O(n) time
// Union of arrays in Objective-C
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSArray *arr1 = @[@1, @2, @3];
NSArray *arr2 = @[@3, @4, @5];
// Using NSSet
NSMutableSet *set = [NSMutableSet setWithArray:arr1];
[set addObjectsFromArray:arr2];
NSArray *unionArray = [set allObjects];
NSLog(@"%@", unionArray); // [1, 2, 3, 4, 5] (order not preserved)
// Preserving order
NSArray *unionPreserveOrder(NSArray *a, NSArray *b) {
NSMutableArray *result = [NSMutableArray arrayWithArray:a];
for (id obj in b) {
if (![result containsObject:obj]) {
[result addObject:obj];
}
}
return result;
}
NSLog(@"%@", unionPreserveOrder(arr1, arr2));
// Using NSOrderedSet
NSArray *unionOrdered = [[NSOrderedSet orderedSetWithArray:[arr1 arrayByAddingObjectsFromArray:arr2]] array];
NSLog(@"%@", unionOrdered);
}
return 0;
}Find elements in first array not in second using NSSet.
- NSSet:
[setA minusSet:setB] - Manual: Check if in set
- Symmetric difference: Union of differences
- Complexity: O(n) time
// Difference of arrays in Objective-C
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSArray *arr1 = @[@1, @2, @3, @4];
NSArray *arr2 = @[@3, @4, @5, @6];
// Using NSSet
NSMutableSet *set1 = [NSMutableSet setWithArray:arr1];
NSSet *set2 = [NSSet setWithArray:arr2];
[set1 minusSet:set2];
NSArray *diff = [set1 allObjects];
NSLog(@"%@", diff); // [1, 2]
// Manual difference
NSArray *differenceManual(NSArray *a, NSArray *b) {
NSMutableSet *setB = [NSMutableSet setWithArray:b];
NSMutableArray *result = [NSMutableArray array];
for (id obj in a) {
if (![setB containsObject:obj]) {
[result addObject:obj];
}
}
return result;
}
NSLog(@"%@", differenceManual(arr1, arr2));
// Symmetric difference
NSArray *symmetricDifference(NSArray *a, NSArray *b) {
NSMutableSet *setA = [NSMutableSet setWithArray:a];
NSMutableSet *setB = [NSMutableSet setWithArray:b];
NSMutableSet *result = [NSMutableSet setWithSet:setA];
[result unionSet:setB];
[result minusSet:[setA intersectsSet:setB]];
return [result allObjects];
}
NSLog(@"%@", symmetricDifference(arr1, arr2));
}
return 0;
}Group objects by property using NSMutableDictionary.
- Dictionary: Group by key
- Key-Value: Use property value as key
- Filtering: Use NSPredicate
- Complexity: O(n) time
// Group by property in Objective-C
#import <Foundation/Foundation.h>
// Data class
@interface Item : NSObject
@property (nonatomic, strong) NSString *type;
@property (nonatomic, strong) NSString *name;
- (instancetype)initWithType:(NSString *)type name:(NSString *)name;
@end
@implementation Item
- (instancetype)initWithType:(NSString *)type name:(NSString *)name {
self = [super init];
if (self) {
_type = type;
_name = name;
}
return self;
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSArray *items = @[
[[Item alloc] initWithType:@"fruit" name:@"apple"],
[[Item alloc] initWithType:@"fruit" name:@"banana"],
[[Item alloc] initWithType:@"veg" name:@"carrot"]
];
// Group by type
NSMutableDictionary *groups = [NSMutableDictionary dictionary];
for (Item *item in items) {
NSMutableArray *group = groups[item.type];
if (!group) {
group = [NSMutableArray array];
groups[item.type] = group;
}
[group addObject:item];
}
for (NSString *key in groups) {
NSLog(@"%@: %@", key, groups[key]);
}
// Using valueForKeyPath
NSDictionary *grouped = [NSDictionary dictionaryWithObjects:items forKeys:[items valueForKeyPath:@"@distinctUnionOfObjects.type"]];
NSLog(@"%@", grouped);
// Using filter
NSArray *fruits = [items filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"type == 'fruit'"]];
NSLog(@"Fruits: %@", fruits);
}
return 0;
}Deep clone using NSCopying protocol and recursive copying.
- NSCopying: Implement
copyWithZone: - Recursive: Copy nested objects
- NSObject:
copymethod - Mutable copy:
mutableCopy
// Deep clone object in Objective-C
#import <Foundation/Foundation.h>
// Data classes
@interface Address : NSObject <NSCopying>
@property (nonatomic, strong) NSString *city;
@property (nonatomic, strong) NSString *zip;
- (instancetype)initWithCity:(NSString *)city zip:(NSString *)zip;
@end
@implementation Address
- (instancetype)initWithCity:(NSString *)city zip:(NSString *)zip {
self = [super init];
if (self) {
_city = city;
_zip = zip;
}
return self;
}
- (id)copyWithZone:(NSZone *)zone {
return [[Address allocWithZone:zone] initWithCity:self.city zip:self.zip];
}
@end
@interface User : NSObject <NSCopying>
@property (nonatomic, strong) NSString *name;
@property (nonatomic, strong) Address *address;
- (instancetype)initWithName:(NSString *)name address:(Address *)address;
@end
@implementation User
- (instancetype)initWithName:(NSString *)name address:(Address *)address {
self = [super init];
if (self) {
_name = name;
_address = address;
}
return self;
}
- (id)copyWithZone:(NSZone *)zone {
User *copy = [[User allocWithZone:zone] initWithName:self.name address:[self.address copy]];
return copy;
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
Address *address = [[Address alloc] initWithCity:@"NYC" zip:@"10001"];
User *original = [[User alloc] initWithName:@"Alice" address:address];
User *cloned = [original copy];
cloned.name = @"Bob";
cloned.address.city = @"LA";
NSLog(@"Original: %@", original.name); // Alice
NSLog(@"Cloned: %@", cloned.name); // Bob
NSLog(@"Original address: %@", original.address.city); // NYC
NSLog(@"Cloned address: %@", cloned.address.city); // LA
}
return 0;
}Perform immutable updates using mutableCopy and recursive updates.
- MutableCopy: Create mutable copy
- Recursive: Update nested structures
- Path: Use dot notation
- Return: New immutable dictionary
// Immutable update in Objective-C
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSDictionary *state = @{
@"user": @{
@"name": @"Alice",
@"age": @25
}
};
// Immutable update using mutableCopy
NSMutableDictionary *newState = [state mutableCopy];
NSMutableDictionary *user = [newState[@"user"] mutableCopy];
user[@"age"] = @26;
newState[@"user"] = user;
NSLog(@"Original: %@", state[@"user"][@"age"]); // 25
NSLog(@"New: %@", newState[@"user"][@"age"]); // 26
// Helper function
NSDictionary *updateImmutable(NSDictionary *dict, NSString *path, id value) {
NSArray *parts = [path componentsSeparatedByString:@"."];
if (parts.count == 1) {
NSMutableDictionary *newDict = [dict mutableCopy];
newDict[parts[0]] = value;
return newDict;
}
NSString *first = parts[0];
NSString *rest = [[parts subarrayWithRange:NSMakeRange(1, parts.count - 1)] componentsJoinedByString:@"."];
NSDictionary *nested = dict[first] ?: @{};
NSDictionary *updatedNested = updateImmutable(nested, rest, value);
NSMutableDictionary *newDict = [dict mutableCopy];
newDict[first] = updatedNested;
return newDict;
}
NSDictionary *newState2 = updateImmutable(state, @"user.age", @26);
NSLog(@"New state: %@", newState2);
}
return 0;
}Pipe composes functions from left to right using blocks.
- Variadic arguments: Using va_list
- Block chain: Apply blocks sequentially
- Return: Final result
- Direction: Left to right
// Pipe function in Objective-C
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
@autoreleasepool {
// Pipe function
id pipe(id value, ...) {
va_list args;
va_start(args, value);
id result = value;
id (^block)(id);
while ((block = va_arg(args, id))) {
result = block(result);
}
va_end(args);
return result;
}
// Functions
id (^doubleBlock)(id) = ^id(id x) {
return @([x integerValue] * 2);
};
id (^addTen)(id) = ^id(id x) {
return @([x integerValue] + 10);
};
id (^square)(id) = ^id(id x) {
NSInteger val = [x integerValue];
return @(val * val);
};
// Usage
id result = pipe(@5, doubleBlock, addTen, square, nil);
NSLog(@"%@", result); // (5*2+10)^2 = 400
// Using NSArray
NSArray *functions = @[doubleBlock, addTen, square];
id result2 = @5;
for (id (^block)(id) in functions) {
result2 = block(result2);
}
NSLog(@"%@", result2);
}
return 0;
}Compose functions from right to left using blocks.
- Reverse: Apply blocks in reverse order
- Block chain: Compose blocks
- Return: Composed function
- Direction: Right to left
// Compose function in Objective-C
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
@autoreleasepool {
// Compose function (right to left)
id (^compose)(NSArray *) = ^id(NSArray *functions) {
return ^id(id value) {
id result = value;
for (id (^block)(id) in [functions reverseObjectEnumerator]) {
result = block(result);
}
return result;
};
};
// Functions
id (^doubleBlock)(id) = ^id(id x) {
return @([x integerValue] * 2);
};
id (^addTen)(id) = ^id(id x) {
return @([x integerValue] + 10);
};
id (^square)(id) = ^id(id x) {
NSInteger val = [x integerValue];
return @(val * val);
};
// Usage
id (^process)(id) = compose(@[doubleBlock, addTen, square]);
id result = process(@5);
NSLog(@"%@", result); // (5*2+10)^2 = 400
}
return 0;
}Cache function results based on arguments using NSMutableDictionary.
- Cache: NSMutableDictionary
- Key: String representation of arguments
- Return: Cached or computed result
- Trade-off: Memory for speed
// Memoization in Objective-C
#import <Foundation/Foundation.h>
@interface Memoizer : NSObject
@property (nonatomic, strong) NSMutableDictionary *cache;
- (id (^)(id))memoize:(id (^)(id))fn;
@end
@implementation Memoizer
- (instancetype)init {
self = [super init];
if (self) {
_cache = [NSMutableDictionary dictionary];
}
return self;
}
- (id (^)(id))memoize:(id (^)(id))fn {
__weak typeof(self) weakSelf = self;
return ^id(id arg) {
NSString *key = [NSString stringWithFormat:@"%@", arg];
id cached = weakSelf.cache[key];
if (cached) {
return cached;
}
id result = fn(arg);
weakSelf.cache[key] = result;
return result;
};
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
Memoizer *memoizer = [[Memoizer alloc] init];
// Fibonacci with memoization
__block id (^fib)(id) = nil;
fib = memoizer.memoize(^id(id n) {
NSInteger val = [n integerValue];
if (val <= 1) return @(val);
return @([fib(@(val - 1)) integerValue] + [fib(@(val - 2)) integerValue]);
});
NSLog(@"%@", fib(@10)); // 55
// Cache usage
NSLog(@"Cache: %@", memoizer.cache);
}
return 0;
}Ensure a function is called only once using dispatch_once or a flag.
- dispatch_once: Thread-safe
- Flag: Track if called
- Result: Cache the result
- Use case: Initialization
// Once function in Objective-C
#import <Foundation/Foundation.h>
// Once function using dispatch_once
id onceFunction(id (^block)(void)) {
static dispatch_once_t onceToken;
static id result = nil;
dispatch_once(&onceToken, ^{
result = block();
});
return result;
}
// Once function using a flag
id onceFunctionWithFlag(id (^block)(void)) {
static BOOL called = NO;
static id result = nil;
@synchronized(self) {
if (!called) {
called = YES;
result = block();
}
}
return result;
}
// Once class
@interface Once : NSObject
@property (nonatomic, copy) id (^block)(void);
@property (nonatomic, assign) BOOL called;
@property (nonatomic, strong) id result;
- (instancetype)initWithBlock:(id (^)(void))block;
- (id)execute;
@end
@implementation Once
- (instancetype)initWithBlock:(id (^)(void))block {
self = [super init];
if (self) {
_block = block;
_called = NO;
_result = nil;
}
return self;
}
- (id)execute {
@synchronized(self) {
if (!self.called) {
self.called = YES;
self.result = self.block();
}
return self.result;
}
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
// Using dispatch_once
id result1 = onceFunction(^{
NSLog(@"Initialized");
return @{@"id": @1, @"name": @"App"};
});
NSLog(@"%@", result1);
// Using Once class
Once *once = [[Once alloc] initWithBlock:^{
NSLog(@"Initialized 2");
return @{@"id": @2, @"name": @"App2"};
}];
NSLog(@"%@", [once execute]);
NSLog(@"%@", [once execute]); // Returns cached
}
return 0;
}Debounce with leading edge using timer and timestamp tracking.
- Timer: NSTimer for delayed execution
- Leading edge: Execute immediately
- Cooldown: Wait before next execution
- Use case: Save actions, API calls
// Debounce with leading edge in Objective-C
#import <Foundation/Foundation.h>
@interface Debouncer : NSObject
@property (nonatomic, assign) NSTimeInterval delay;
@property (nonatomic, strong) void (^block)(void);
@property (nonatomic, strong) NSTimer *timer;
@property (nonatomic, assign) NSTimeInterval lastCall;
- (instancetype)initWithDelay:(NSTimeInterval)delay block:(void (^)(void))block;
- (void)call;
@end
@implementation Debouncer
- (instancetype)initWithDelay:(NSTimeInterval)delay block:(void (^)(void))block {
self = [super init];
if (self) {
_delay = delay;
_block = block;
_lastCall = 0;
}
return self;
}
- (void)call {
NSTimeInterval now = [[NSDate date] timeIntervalSince1970];
if (now - self.lastCall < self.delay) {
[self.timer invalidate];
self.timer = [NSTimer scheduledTimerWithTimeInterval:self.delay
target:self
selector:@selector(timerFired)
userInfo:nil
repeats:NO];
} else {
self.lastCall = now;
self.block();
}
}
- (void)timerFired {
self.lastCall = [[NSDate date] timeIntervalSince1970];
self.block();
self.timer = nil;
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
Debouncer *debouncer = [[Debouncer alloc] initWithDelay:1.0 block:^{
NSLog(@"Executed");
}];
[debouncer call]; // Executes immediately
[debouncer call]; // Scheduled for later
[debouncer call]; // Scheduled for later
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:2.0]];
}
return 0;
}Throttle with leading edge using timestamp tracking.
- Timestamp: Track last execution time
- Leading edge: Execute if enough time passed
- Rate limiting: At most once per period
- Use case: Scroll events, resize
// Throttle with leading edge in Objective-C
#import <Foundation/Foundation.h>
@interface Throttler : NSObject
@property (nonatomic, assign) NSTimeInterval delay;
@property (nonatomic, strong) void (^block)(void);
@property (nonatomic, assign) NSTimeInterval lastCall;
- (instancetype)initWithDelay:(NSTimeInterval)delay block:(void (^)(void))block;
- (void)call;
@end
@implementation Throttler
- (instancetype)initWithDelay:(NSTimeInterval)delay block:(void (^)(void))block {
self = [super init];
if (self) {
_delay = delay;
_block = block;
_lastCall = 0;
}
return self;
}
- (void)call {
NSTimeInterval now = [[NSDate date] timeIntervalSince1970];
if (now - self.lastCall >= self.delay) {
self.lastCall = now;
self.block();
}
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
Throttler *throttler = [[Throttler alloc] initWithDelay:1.0 block:^{
NSLog(@"Executed");
}];
[throttler call]; // Executes
[throttler call]; // Ignored (within 1 second)
[throttler call]; // Ignored (within 1 second)
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:2.0]];
}
return 0;
}Deep equality comparison using recursion for nested structures.
- Recursive: Compare nested structures
- Base cases: Primitive values
- Arrays: Compare elements recursively
- Dictionaries: Compare key-value pairs
// Deep equal in Objective-C
#import <Foundation/Foundation.h>
BOOL deepEqual(id obj1, id obj2) {
if (obj1 == obj2) return YES;
if (!obj1 || !obj2) return NO;
if ([obj1 class] != [obj2 class]) return NO;
if ([obj1 isKindOfClass:[NSString class]] ||
[obj1 isKindOfClass:[NSNumber class]] ||
[obj1 isKindOfClass:[NSDate class]]) {
return [obj1 isEqual:obj2];
}
if ([obj1 isKindOfClass:[NSArray class]]) {
NSArray *arr1 = obj1;
NSArray *arr2 = obj2;
if (arr1.count != arr2.count) return NO;
for (NSInteger i = 0; i < arr1.count; i++) {
if (!deepEqual(arr1[i], arr2[i])) return NO;
}
return YES;
}
if ([obj1 isKindOfClass:[NSDictionary class]]) {
NSDictionary *dict1 = obj1;
NSDictionary *dict2 = obj2;
if (dict1.count != dict2.count) return NO;
for (id key in dict1) {
if (!dict2[key]) return NO;
if (!deepEqual(dict1[key], dict2[key])) return NO;
}
return YES;
}
// For custom objects
if ([obj1 respondsToSelector:@selector(deepEqual:)]) {
return [obj1 performSelector:@selector(deepEqual:) withObject:obj2];
}
return [obj1 isEqual:obj2];
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSDictionary *obj1 = @{@"name": @"Alice", @"address": @{@"city": @"NYC"}};
NSDictionary *obj2 = @{@"name": @"Alice", @"address": @{@"city": @"NYC"}};
NSDictionary *obj3 = @{@"name": @"Bob", @"address": @{@"city": @"LA"}};
NSLog(@"%d", deepEqual(obj1, obj2)); // 1 (true)
NSLog(@"%d", deepEqual(obj1, obj3)); // 0 (false)
}
return 0;
}Observable pattern using NSNotificationCenter or custom implementation.
- Observable: Maintains subscribers
- Subscribe: Add callback
- Notify: Call all subscribers
- Unsubscribe: Remove callback
// Observable pattern in Objective-C
#import <Foundation/Foundation.h>
// Observable class
@interface Observable : NSObject
@property (nonatomic, strong) NSMutableArray *subscribers;
- (void)subscribe:(void (^)(id))callback;
- (void)notify:(id)data;
@end
@implementation Observable
- (instancetype)init {
self = [super init];
if (self) {
_subscribers = [NSMutableArray array];
}
return self;
}
- (void)subscribe:(void (^)(id))callback {
[self.subscribers addObject:callback];
}
- (void)notify:(id)data {
for (void (^callback)(id) in self.subscribers) {
callback(data);
}
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
Observable *observable = [[Observable alloc] init];
[observable subscribe:^(id data) {
NSLog(@"Observer 1 received: %@", data);
}];
[observable subscribe:^(id data) {
NSLog(@"Observer 2 received: %@", data);
}];
[observable notify:@"Hello World"];
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.5]];
}
return 0;
}Singleton pattern using dispatch_once for thread-safe initialization.
- dispatch_once: Thread-safe initialization
- sharedInstance: Class method
- Prevent copying: Override copy methods
- Global access: Through shared instance
// Singleton pattern in Objective-C
#import <Foundation/Foundation.h>
@interface Singleton : NSObject
+ (instancetype)sharedInstance;
@property (nonatomic, strong) NSMutableDictionary *data;
- (void)setObject:(id)object forKey:(NSString *)key;
- (id)objectForKey:(NSString *)key;
@end
@implementation Singleton
+ (instancetype)sharedInstance {
static Singleton *sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[self alloc] init];
sharedInstance.data = [NSMutableDictionary dictionary];
});
return sharedInstance;
}
- (void)setObject:(id)object forKey:(NSString *)key {
self.data[key] = object;
}
- (id)objectForKey:(NSString *)key {
return self.data[key];
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
Singleton *s1 = [Singleton sharedInstance];
Singleton *s2 = [Singleton sharedInstance];
[s1 setObject:@"Alice" forKey:@"name"];
NSLog(@"%@", [s2 objectForKey:@"name"]); // Alice
NSLog(@"%d", s1 == s2); // 1 (true)
}
return 0;
}Factory pattern using class methods to create objects.
- Factory method: Class method that creates objects
- Type parameter: Determines which class to create
- Return: Instance of appropriate class
- Benefits: Decouples creation logic
// Factory pattern in Objective-C
#import <Foundation/Foundation.h>
// Base class
@interface User : NSObject
@property (nonatomic, strong) NSString *name;
- (NSString *)getRole;
@end
@implementation User
- (NSString *)getRole { return @"user"; }
@end
// Subclasses
@interface Admin : User @end
@implementation Admin
- (NSString *)getRole { return @"admin"; }
@end
@interface Guest : User @end
@implementation Guest
- (NSString *)getRole { return @"guest"; }
@end
// Factory
@interface UserFactory : NSObject
+ (User *)createUserWithType:(NSString *)type name:(NSString *)name;
@end
@implementation UserFactory
+ (User *)createUserWithType:(NSString *)type name:(NSString *)name {
User *user = nil;
if ([type isEqualToString:@"admin"]) {
user = [[Admin alloc] init];
} else if ([type isEqualToString:@"guest"]) {
user = [[Guest alloc] init];
} else {
user = [[User alloc] init];
}
user.name = name;
return user;
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
User *admin = [UserFactory createUserWithType:@"admin" name:@"Alice"];
User *guest = [UserFactory createUserWithType:@"guest" name:@"Bob"];
NSLog(@"%@ role: %@", admin.name, [admin getRole]);
NSLog(@"%@ role: %@", guest.name, [guest getRole]);
}
return 0;
}Strategy pattern using protocols and composition.
- Strategy protocol: Defines algorithm interface
- Context: Uses strategy
- Runtime switching: Change strategy at runtime
- Benefits: Encapsulate algorithms
// Strategy pattern in Objective-C
#import <Foundation/Foundation.h>
// Strategy protocol
@protocol PaymentStrategy <NSObject>
- (void)pay:(double)amount;
@end
// Concrete strategies
@interface CreditCardStrategy : NSObject <PaymentStrategy> @end
@implementation CreditCardStrategy
- (void)pay:(double)amount {
NSLog(@"Paid $%.2f with Credit Card", amount);
}
@end
@interface PayPalStrategy : NSObject <PaymentStrategy> @end
@implementation PayPalStrategy
- (void)pay:(double)amount {
NSLog(@"Paid $%.2f with PayPal", amount);
}
@end
// Context
@interface PaymentContext : NSObject
@property (nonatomic, strong) id<PaymentStrategy> strategy;
- (instancetype)initWithStrategy:(id<PaymentStrategy>)strategy;
- (void)executePayment:(double)amount;
@end
@implementation PaymentContext
- (instancetype)initWithStrategy:(id<PaymentStrategy>)strategy {
self = [super init];
if (self) {
_strategy = strategy;
}
return self;
}
- (void)executePayment:(double)amount {
[self.strategy pay:amount];
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
PaymentContext *context = [[PaymentContext alloc] initWithStrategy:[[CreditCardStrategy alloc] init]];
[context executePayment:100.0];
context.strategy = [[PayPalStrategy alloc] init];
[context executePayment:50.0];
}
return 0;
}Observer pattern using protocols and custom implementation.
- Observer protocol: Defines update method
- Subject: Maintains observers
- Attach/Detach: Add/remove observers
- Notify: Call update on all observers
// Observer pattern in Objective-C
#import <Foundation/Foundation.h>
@protocol Observer <NSObject>
- (void)update:(NSString *)data;
@end
@interface Subject : NSObject
@property (nonatomic, strong) NSMutableArray *observers;
@property (nonatomic, strong) NSString *state;
- (void)attach:(id<Observer>)observer;
- (void)detach:(id<Observer>)observer;
- (void)setState:(NSString *)state;
@end
@implementation Subject
- (instancetype)init {
self = [super init];
if (self) {
_observers = [NSMutableArray array];
_state = @"";
}
return self;
}
- (void)attach:(id<Observer>)observer {
[self.observers addObject:observer];
}
- (void)detach:(id<Observer>)observer {
[self.observers removeObject:observer];
}
- (void)setState:(NSString *)state {
_state = state;
for (id<Observer> observer in self.observers) {
[observer update:state];
}
}
@end
@interface ConcreteObserver : NSObject <Observer>
@property (nonatomic, strong) NSString *name;
- (instancetype)initWithName:(NSString *)name;
@end
@implementation ConcreteObserver
- (instancetype)initWithName:(NSString *)name {
self = [super init];
if (self) {
_name = name;
}
return self;
}
- (void)update:(NSString *)data {
NSLog(@"%@ received: %@", self.name, data);
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
Subject *subject = [[Subject alloc] init];
ConcreteObserver *obs1 = [[ConcreteObserver alloc] initWithName:@"Observer1"];
ConcreteObserver *obs2 = [[ConcreteObserver alloc] initWithName:@"Observer2"];
[subject attach:obs1];
[subject attach:obs2];
[subject setState:@"Hello World"];
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.5]];
}
return 0;
}Decorator pattern using wrapper functions or classes.
- Component: Base object
- Decorator: Wraps component
- Chaining: Multiple decorators
- Benefits: Add behavior dynamically
// Decorator pattern in Objective-C
#import <Foundation/Foundation.h>
@interface Coffee : NSObject
@property (nonatomic, assign) double cost;
@property (nonatomic, strong) NSString *desc;
- (instancetype)initWithCost:(double)cost description:(NSString *)desc;
@end
@implementation Coffee
- (instancetype)initWithCost:(double)cost description:(NSString *)desc {
self = [super init];
if (self) {
_cost = cost;
_desc = desc;
}
return self;
}
@end
// Decorators
Coffee *milkDecorator(Coffee *coffee) {
return [[Coffee alloc] initWithCost:coffee.cost + 2.0
description:[coffee.desc stringByAppendingString:@", Milk"]];
}
Coffee *sugarDecorator(Coffee *coffee) {
return [[Coffee alloc] initWithCost:coffee.cost + 1.0
description:[coffee.desc stringByAppendingString:@", Sugar"]];
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
Coffee *coffee = [[Coffee alloc] initWithCost:5.0 description:@"Coffee"];
coffee = milkDecorator(coffee);
coffee = sugarDecorator(coffee);
NSLog(@"%@", coffee.desc); // Coffee, Milk, Sugar
NSLog(@"%.2f", coffee.cost); // 8.0
}
return 0;
}Command pattern with execute and undo methods.
- Command protocol: Execute and undo methods
- Receiver: Performs actual work
- Invoker: Executes commands
- Undo/Redo: Command history
// Command pattern in Objective-C
#import <Foundation/Foundation.h>
@protocol Command <NSObject>
- (void)execute;
- (void)undo;
@end
@interface AddCommand : NSObject <Command>
@property (nonatomic, strong) NSMutableArray *receiver;
@property (nonatomic, strong) id value;
- (instancetype)initWithReceiver:(NSMutableArray *)receiver value:(id)value;
@end
@implementation AddCommand
- (instancetype)initWithReceiver:(NSMutableArray *)receiver value:(id)value {
self = [super init];
if (self) {
_receiver = receiver;
_value = value;
}
return self;
}
- (void)execute {
[self.receiver addObject:self.value];
}
- (void)undo {
[self.receiver removeObject:self.value];
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSMutableArray *receiver = [NSMutableArray arrayWithArray:@[@1, @2, @3]];
AddCommand *cmd = [[AddCommand alloc] initWithReceiver:receiver value:@4];
[cmd execute];
NSLog(@"%@", receiver); // [1, 2, 3, 4]
[cmd undo];
NSLog(@"%@", receiver); // [1, 2, 3]
}
return 0;
}Memento pattern for state capture and restoration.
- Originator: Creates and restores mementos
- Memento: Stores state
- Caretaker: Manages mementos
- Undo/Redo: State history
// Memento pattern in Objective-C
#import <Foundation/Foundation.h>
@interface Memento : NSObject
@property (nonatomic, strong) NSDictionary *state;
- (instancetype)initWithState:(NSDictionary *)state;
@end
@implementation Memento
- (instancetype)initWithState:(NSDictionary *)state {
self = [super init];
if (self) {
_state = state;
}
return self;
}
@end
@interface Originator : NSObject
@property (nonatomic, strong) NSDictionary *state;
- (Memento *)saveState;
- (void)restoreState:(Memento *)memento;
@end
@implementation Originator
- (Memento *)saveState {
return [[Memento alloc] initWithState:self.state];
}
- (void)restoreState:(Memento *)memento {
self.state = memento.state;
}
@end
@interface Caretaker : NSObject
@property (nonatomic, strong) NSMutableArray *mementos;
- (void)addMemento:(Memento *)memento;
- (Memento *)getMementoAtIndex:(NSInteger)index;
@end
@implementation Caretaker
- (instancetype)init {
self = [super init];
if (self) {
_mementos = [NSMutableArray array];
}
return self;
}
- (void)addMemento:(Memento *)memento {
[self.mementos addObject:memento];
}
- (Memento *)getMementoAtIndex:(NSInteger)index {
return self.mementos[index];
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
Originator *originator = [[Originator alloc] init];
Caretaker *caretaker = [[Caretaker alloc] init];
originator.state = @{@"name": @"State 1"};
[caretaker addMemento:[originator saveState]];
originator.state = @{@"name": @"State 2"};
[caretaker addMemento:[originator saveState]];
originator.state = @{@"name": @"State 3"};
[originator restoreState:[caretaker getMementoAtIndex:0]];
NSLog(@"%@", originator.state); // {name: State 1}
}
return 0;
}Mediator pattern for centralized communication.
- Mediator: Encapsulates communication
- Colleague: Communicates through mediator
- Benefits: Loose coupling
- Use case: Chat systems
// Mediator pattern in Objective-C
#import <Foundation/Foundation.h>
@class Colleague;
@interface Mediator : NSObject
@property (nonatomic, strong) NSMutableArray *colleagues;
- (void)registerColleague:(Colleague *)colleague;
- (void)sendMessage:(NSString *)message fromSender:(Colleague *)sender;
@end
@implementation Mediator
- (instancetype)init {
self = [super init];
if (self) {
_colleagues = [NSMutableArray array];
}
return self;
}
- (void)registerColleague:(Colleague *)colleague {
[self.colleagues addObject:colleague];
colleague.mediator = self;
}
- (void)sendMessage:(NSString *)message fromSender:(Colleague *)sender {
for (Colleague *col in self.colleagues) {
if (col != sender) {
[col receiveMessage:message];
}
}
}
@end
@interface Colleague : NSObject
@property (nonatomic, strong) NSString *name;
@property (nonatomic, weak) Mediator *mediator;
- (instancetype)initWithName:(NSString *)name;
- (void)sendMessage:(NSString *)message;
- (void)receiveMessage:(NSString *)message;
@end
@implementation Colleague
- (instancetype)initWithName:(NSString *)name {
self = [super init];
if (self) {
_name = name;
}
return self;
}
- (void)sendMessage:(NSString *)message {
[self.mediator sendMessage:message fromSender:self];
}
- (void)receiveMessage:(NSString *)message {
NSLog(@"%@ received: %@", self.name, message);
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
Mediator *mediator = [[Mediator alloc] init];
Colleague *alice = [[Colleague alloc] initWithName:@"Alice"];
Colleague *bob = [[Colleague alloc] initWithName:@"Bob"];
[mediator registerColleague:alice];
[mediator registerColleague:bob];
[alice sendMessage:@"Hello Bob!"];
}
return 0;
}Chain of Responsibility for processing requests sequentially.
- Handler: Processes or forwards
- Chain: Linked list of handlers
- Benefits: Decoupling
- Use case: Logging, authentication
// Chain of Responsibility in Objective-C
#import <Foundation/Foundation.h>
@interface Handler : NSObject
@property (nonatomic, strong) Handler *nextHandler;
- (void)setNextHandler:(Handler *)handler;
- (void)handleRequest:(NSDictionary *)request;
@end
@implementation Handler
- (void)setNextHandler:(Handler *)handler {
self.nextHandler = handler;
}
- (void)handleRequest:(NSDictionary *)request {
if (self.nextHandler) {
[self.nextHandler handleRequest:request];
}
}
@end
@interface AuthHandler : Handler
@end
@implementation AuthHandler
- (void)handleRequest:(NSDictionary *)request {
if (request[@"token"]) {
NSLog(@"Authentication passed");
[super handleRequest:request];
} else {
NSLog(@"Authentication failed");
}
}
@end
@interface LoggerHandler : Handler
@end
@implementation LoggerHandler
- (void)handleRequest:(NSDictionary *)request {
NSLog(@"Logging request: %@", request[@"url"]);
[super handleRequest:request];
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
AuthHandler *auth = [[AuthHandler alloc] init];
LoggerHandler *logger = [[LoggerHandler alloc] init];
[auth setNextHandler:logger];
[auth handleRequest:@{@"token": @"valid", @"url": @"/api"}];
}
return 0;
}State pattern for changing behavior with state.
- Context: Maintains state
- State: Defines behavior
- Transitions: Change between states
- Benefits: Clean state management
// State pattern in Objective-C
#import <Foundation/Foundation.h>
@class Context;
@protocol State <NSObject>
- (void)handle:(Context *)context;
@end
@interface ReadyState : NSObject <State> @end
@implementation ReadyState
- (void)handle:(Context *)context {
NSLog(@"Ready: Waiting for input");
context.state = [[ProcessingState alloc] init];
}
@end
@interface ProcessingState : NSObject <State> @end
@implementation ProcessingState
- (void)handle:(Context *)context {
NSLog(@"Processing: Working on task");
context.state = [[CompletedState alloc] init];
}
@end
@interface CompletedState : NSObject <State> @end
@implementation CompletedState
- (void)handle:(Context *)context {
NSLog(@"Completed: Task finished");
}
@end
@interface Context : NSObject
@property (nonatomic, strong) id<State> state;
- (void)request;
@end
@implementation Context
- (instancetype)init {
self = [super init];
if (self) {
_state = [[ReadyState alloc] init];
}
return self;
}
- (void)request {
[self.state handle:self];
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
Context *context = [[Context alloc] init];
[context request]; // Ready
[context request]; // Processing
[context request]; // Completed
}
return 0;
}Proxy pattern for controlling access to objects.
- Subject: Real object
- Proxy: Controls access
- Lazy loading: Create on demand
- Benefits: Access control, logging
// Proxy pattern in Objective-C
#import <Foundation/Foundation.h>
@protocol Subject <NSObject>
- (void)request;
@end
@interface RealSubject : NSObject <Subject> @end
@implementation RealSubject
- (void)request {
NSLog(@"RealSubject: Handling request");
}
@end
@interface Proxy : NSObject <Subject>
@property (nonatomic, strong) RealSubject *realSubject;
@end
@implementation Proxy
- (void)request {
if ([self checkAccess]) {
if (!self.realSubject) {
self.realSubject = [[RealSubject alloc] init];
}
[self.realSubject request];
[self logAccess];
}
}
- (BOOL)checkAccess {
NSLog(@"Proxy: Checking access");
return YES;
}
- (void)logAccess {
NSLog(@"Proxy: Logging access");
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
Proxy *proxy = [[Proxy alloc] init];
[proxy request];
}
return 0;
}Flyweight pattern for sharing objects to save memory.
- Flyweight: Shared object
- Factory: Manages flyweights
- Benefits: Memory optimization
- Use case: Character rendering
// Flyweight pattern in Objective-C
#import <Foundation/Foundation.h>
@interface Flyweight : NSObject
@property (nonatomic, strong) NSString *sharedState;
- (instancetype)initWithState:(NSString *)state;
- (void)operation:(NSString *)uniqueState;
@end
@implementation Flyweight
- (instancetype)initWithState:(NSString *)state {
self = [super init];
if (self) {
_sharedState = state;
}
return self;
}
- (void)operation:(NSString *)uniqueState {
NSLog(@"Shared: %@, Unique: %@", self.sharedState, uniqueState);
}
@end
@interface FlyweightFactory : NSObject
@property (nonatomic, strong) NSMutableDictionary *flyweights;
- (Flyweight *)getFlyweight:(NSString *)sharedState;
@end
@implementation FlyweightFactory
- (instancetype)init {
self = [super init];
if (self) {
_flyweights = [NSMutableDictionary dictionary];
}
return self;
}
- (Flyweight *)getFlyweight:(NSString *)sharedState {
if (!self.flyweights[sharedState]) {
self.flyweights[sharedState] = [[Flyweight alloc] initWithState:sharedState];
NSLog(@"Creating new flyweight for: %@", sharedState);
}
return self.flyweights[sharedState];
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
FlyweightFactory *factory = [[FlyweightFactory alloc] init];
Flyweight *fw1 = [factory getFlyweight:@"state1"];
Flyweight *fw2 = [factory getFlyweight:@"state1"];
Flyweight *fw3 = [factory getFlyweight:@"state2"];
[fw1 operation:@"unique1"];
[fw2 operation:@"unique2"];
[fw3 operation:@"unique3"];
}
return 0;
}Bridge pattern for separating abstraction from implementation.
- Abstraction: High-level interface
- Implementation: Low-level operations
- Benefits: Separation of concerns
- Use case: Cross-platform
// Bridge pattern in Objective-C
#import <Foundation/Foundation.h>
@protocol Implementation <NSObject>
- (void)operationImpl;
@end
@interface ConcreteImplementationA : NSObject <Implementation> @end
@implementation ConcreteImplementationA
- (void)operationImpl {
NSLog(@"ConcreteImplementationA: Operation");
}
@end
@interface ConcreteImplementationB : NSObject <Implementation> @end
@implementation ConcreteImplementationB
- (void)operationImpl {
NSLog(@"ConcreteImplementationB: Operation");
}
@end
@interface Abstraction : NSObject
@property (nonatomic, strong) id<Implementation> impl;
- (instancetype)initWithImpl:(id<Implementation>)impl;
- (void)operation;
@end
@implementation Abstraction
- (instancetype)initWithImpl:(id<Implementation>)impl {
self = [super init];
if (self) {
_impl = impl;
}
return self;
}
- (void)operation {
NSLog(@"Abstraction: Additional logic");
[self.impl operationImpl];
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
id<Implementation> implA = [[ConcreteImplementationA alloc] init];
id<Implementation> implB = [[ConcreteImplementationB alloc] init];
Abstraction *abs1 = [[Abstraction alloc] initWithImpl:implA];
Abstraction *abs2 = [[Abstraction alloc] initWithImpl:implB];
[abs1 operation];
[abs2 operation];
}
return 0;
}Adapter pattern for converting interfaces.
- Target: Expected interface
- Adaptee: Existing interface
- Adapter: Bridges interfaces
- Benefits: Reusability
// Adapter pattern in Objective-C
#import <Foundation/Foundation.h>
@interface Target : NSObject
- (void)request;
@end
@implementation Target
- (void)request {
NSLog(@"Target: Request");
}
@end
@interface Adaptee : NSObject
- (void)specificRequest;
@end
@implementation Adaptee
- (void)specificRequest {
NSLog(@"Adaptee: Specific Request");
}
@end
@interface Adapter : Target
@property (nonatomic, strong) Adaptee *adaptee;
- (instancetype)initWithAdaptee:(Adaptee *)adaptee;
@end
@implementation Adapter
- (instancetype)initWithAdaptee:(Adaptee *)adaptee {
self = [super init];
if (self) {
_adaptee = adaptee;
}
return self;
}
- (void)request {
[self.adaptee specificRequest];
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
Adaptee *adaptee = [[Adaptee alloc] init];
Adapter *adapter = [[Adapter alloc] initWithAdaptee:adaptee];
[adapter request];
}
return 0;
}Facade pattern for simplifying complex subsystems.
- Facade: Simplified interface
- Subsystem: Complex components
- Benefits: Simplified interface
- Use case: Library APIs
// Facade pattern in Objective-C
#import <Foundation/Foundation.h>
@interface SubsystemA : NSObject
- (void)operationA;
@end
@implementation SubsystemA
- (void)operationA {
NSLog(@"SubsystemA: Operation");
}
@end
@interface SubsystemB : NSObject
- (void)operationB;
@end
@implementation SubsystemB
- (void)operationB {
NSLog(@"SubsystemB: Operation");
}
@end
@interface Facade : NSObject
- (void)operation;
@end
@implementation Facade {
SubsystemA *_subsystemA;
SubsystemB *_subsystemB;
}
- (instancetype)init {
self = [super init];
if (self) {
_subsystemA = [[SubsystemA alloc] init];
_subsystemB = [[SubsystemB alloc] init];
}
return self;
}
- (void)operation {
[_subsystemA operationA];
[_subsystemB operationB];
NSLog(@"Facade: Complex operation");
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
Facade *facade = [[Facade alloc] init];
[facade operation];
}
return 0;
}Composite pattern for tree structures.
- Component: Interface for all
- Leaf: Individual object
- Composite: Container
- Benefits: Uniform interface
// Composite pattern in Objective-C
#import <Foundation/Foundation.h>
@protocol Component <NSObject>
- (void)operation;
@end
@interface Leaf : NSObject <Component>
@property (nonatomic, strong) NSString *name;
- (instancetype)initWithName:(NSString *)name;
@end
@implementation Leaf
- (instancetype)initWithName:(NSString *)name {
self = [super init];
if (self) {
_name = name;
}
return self;
}
- (void)operation {
NSLog(@"Leaf %@: Operation", self.name);
}
@end
@interface Composite : NSObject <Component>
@property (nonatomic, strong) NSString *name;
@property (nonatomic, strong) NSMutableArray *children;
- (instancetype)initWithName:(NSString *)name;
- (void)add:(id<Component>)component;
- (void)remove:(id<Component>)component;
@end
@implementation Composite
- (instancetype)initWithName:(NSString *)name {
self = [super init];
if (self) {
_name = name;
_children = [NSMutableArray array];
}
return self;
}
- (void)add:(id<Component>)component {
[self.children addObject:component];
}
- (void)remove:(id<Component>)component {
[self.children removeObject:component];
}
- (void)operation {
NSLog(@"Composite %@: Operation", self.name);
for (id<Component> child in self.children) {
[child operation];
}
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
Leaf *leaf1 = [[Leaf alloc] initWithName:@"A"];
Leaf *leaf2 = [[Leaf alloc] initWithName:@"B"];
Composite *composite = [[Composite alloc] initWithName:@"Root"];
[composite add:leaf1];
[composite add:leaf2];
[composite operation];
}
return 0;
}Visitor pattern for adding operations without modifying elements.
- Visitor: Defines operations
- Element: Accepts visitors
- Benefits: Adding operations without modifying
- Use case: Compilers, AST
// Visitor pattern in Objective-C
#import <Foundation/Foundation.h>
@class ElementA;
@class ElementB;
@protocol Visitor <NSObject>
- (void)visitElementA:(ElementA *)element;
- (void)visitElementB:(ElementB *)element;
@end
@protocol Element <NSObject>
- (void)accept:(id<Visitor>)visitor;
@end
@interface ElementA : NSObject <Element>
@property (nonatomic, strong) NSString *data;
- (instancetype)initWithData:(NSString *)data;
@end
@implementation ElementA
- (instancetype)initWithData:(NSString *)data {
self = [super init];
if (self) {
_data = data;
}
return self;
}
- (void)accept:(id<Visitor>)visitor {
[visitor visitElementA:self];
}
@end
@interface ElementB : NSObject <Element>
@property (nonatomic, strong) NSString *data;
- (instancetype)initWithData:(NSString *)data;
@end
@implementation ElementB
- (instancetype)initWithData:(NSString *)data {
self = [super init];
if (self) {
_data = data;
}
return self;
}
- (void)accept:(id<Visitor>)visitor {
[visitor visitElementB:self];
}
@end
@interface ConcreteVisitor : NSObject <Visitor>
@end
@implementation ConcreteVisitor
- (void)visitElementA:(ElementA *)element {
NSLog(@"Visiting ElementA with data: %@", element.data);
}
- (void)visitElementB:(ElementB *)element {
NSLog(@"Visiting ElementB with data: %@", element.data);
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
ConcreteVisitor *visitor = [[ConcreteVisitor alloc] init];
ElementA *elemA = [[ElementA alloc] initWithData:@"A data"];
ElementB *elemB = [[ElementB alloc] initWithData:@"B data"];
[elemA accept:visitor];
[elemB accept:visitor];
}
return 0;
}Iterator pattern for sequential access to collections.
- Iterator: Traverses collection
- Aggregate: Creates iterator
- Benefits: Uniform traversal
- Use case: Collection traversal
// Iterator pattern in Objective-C
#import <Foundation/Foundation.h>
@interface CustomIterator : NSObject
@property (nonatomic, strong) NSArray *collection;
@property (nonatomic, assign) NSInteger index;
- (instancetype)initWithCollection:(NSArray *)collection;
- (id)next;
- (BOOL)hasNext;
@end
@implementation CustomIterator
- (instancetype)initWithCollection:(NSArray *)collection {
self = [super init];
if (self) {
_collection = collection;
_index = 0;
}
return self;
}
- (id)next {
if ([self hasNext]) {
return self.collection[self.index++];
}
return nil;
}
- (BOOL)hasNext {
return self.index < self.collection.count;
}
@end
@interface CustomCollection : NSObject
@property (nonatomic, strong) NSMutableArray *items;
- (void)add:(id)item;
- (CustomIterator *)getIterator;
@end
@implementation CustomCollection
- (instancetype)init {
self = [super init];
if (self) {
_items = [NSMutableArray array];
}
return self;
}
- (void)add:(id)item {
[self.items addObject:item];
}
- (CustomIterator *)getIterator {
return [[CustomIterator alloc] initWithCollection:self.items];
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
CustomCollection *collection = [[CustomCollection alloc] init];
[collection add:@"A"];
[collection add:@"B"];
[collection add:@"C"];
CustomIterator *iterator = [collection getIterator];
while ([iterator hasNext]) {
NSLog(@"%@", [iterator next]);
}
}
return 0;
}Template Method for algorithm skeletons.
- AbstractClass: Defines template
- ConcreteClass: Implements steps
- Benefits: Code reuse
- Use case: Frameworks
// Template Method pattern in Objective-C
#import <Foundation/Foundation.h>
@interface AbstractClass : NSObject
- (void)templateMethod;
- (void)step1;
- (void)step2;
- (void)step3;
@end
@implementation AbstractClass
- (void)templateMethod {
[self step1];
[self step2];
[self step3];
}
- (void)step1 {
NSLog(@"Step 1");
}
- (void)step2 {
// Abstract - to be overridden
}
- (void)step3 {
NSLog(@"Step 3");
}
@end
@interface ConcreteClass : AbstractClass
@end
@implementation ConcreteClass
- (void)step2 {
NSLog(@"Concrete Step 2");
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
ConcreteClass *concrete = [[ConcreteClass alloc] init];
[concrete templateMethod];
}
return 0;
}Builder pattern for constructing complex objects.
- Builder: Constructs parts
- Director: Orchestrates construction
- Product: Constructed object
- Benefits: Step-by-step construction
// Builder pattern in Objective-C
#import <Foundation/Foundation.h>
@interface Product : NSObject
@property (nonatomic, strong) NSMutableArray *parts;
- (void)add:(NSString *)part;
- (void)listParts;
@end
@implementation Product
- (instancetype)init {
self = [super init];
if (self) {
_parts = [NSMutableArray array];
}
return self;
}
- (void)add:(NSString *)part {
[self.parts addObject:part];
}
- (void)listParts {
NSLog(@"%@", [self.parts componentsJoinedByString:@", "]);
}
@end
@interface Builder : NSObject
@property (nonatomic, strong) Product *product;
- (void)reset;
- (void)buildStepA;
- (void)buildStepB;
- (Product *)getResult;
@end
@implementation Builder
- (instancetype)init {
self = [super init];
if (self) {
[self reset];
}
return self;
}
- (void)reset {
self.product = [[Product alloc] init];
}
- (void)buildStepA {
[self.product add:@"Part A"];
}
- (void)buildStepB {
[self.product add:@"Part B"];
}
- (Product *)getResult {
return self.product;
}
@end
@interface Director : NSObject
@property (nonatomic, strong) Builder *builder;
- (instancetype)initWithBuilder:(Builder *)builder;
- (void)buildMinimal;
- (void)buildFull;
@end
@implementation Director
- (instancetype)initWithBuilder:(Builder *)builder {
self = [super init];
if (self) {
_builder = builder;
}
return self;
}
- (void)buildMinimal {
[self.builder buildStepA];
}
- (void)buildFull {
[self.builder buildStepA];
[self.builder buildStepB];
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
Builder *builder = [[Builder alloc] init];
Director *director = [[Director alloc] initWithBuilder:builder];
[director buildMinimal];
Product *product = [builder getResult];
[product listParts]; // Part A
}
return 0;
}Prototype pattern for cloning objects using NSCopying.
- NSCopying: Implement copyWithZone:
- Shallow copy:
copy - Deep copy: Recursive copy
- Benefits: Object reuse, performance
// Prototype pattern in Objective-C
#import <Foundation/Foundation.h>
@interface Prototype : NSObject <NSCopying>
@property (nonatomic, strong) NSString *name;
@property (nonatomic, strong) NSMutableDictionary *nested;
- (instancetype)initWithName:(NSString *)name nested:(NSMutableDictionary *)nested;
@end
@implementation Prototype
- (instancetype)initWithName:(NSString *)name nested:(NSMutableDictionary *)nested {
self = [super init];
if (self) {
_name = name;
_nested = nested;
}
return self;
}
- (id)copyWithZone:(NSZone *)zone {
Prototype *copy = [[Prototype allocWithZone:zone] initWithName:self.name
nested:[self.nested mutableCopy]];
return copy;
}
- (id)deepCopy {
Prototype *copy = [[Prototype alloc] initWithName:self.name
nested:[NSMutableDictionary dictionary]];
for (id key in self.nested) {
id value = self.nested[key];
if ([value respondsToSelector:@selector(copyWithZone:)]) {
copy.nested[key] = [value copy];
} else {
copy.nested[key] = value;
}
}
return copy;
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSMutableDictionary *nested = [NSMutableDictionary dictionaryWithObject:@42 forKey:@"value"];
Prototype *original = [[Prototype alloc] initWithName:@"Original" nested:nested];
Prototype *copy = [original copy];
Prototype *deepCopy = [original deepCopy];
copy.name = @"Copy";
copy.nested[@"value"] = @99;
deepCopy.nested[@"value"] = @100;
NSLog(@"Original name: %@", original.name); // Original
NSLog(@"Original nested: %@", original.nested); // {value: 42}
NSLog(@"Copy name: %@", copy.name); // Copy
NSLog(@"DeepCopy nested: %@", deepCopy.nested); // {value: 100}
}
return 0;
}Archiving using NSCoding protocol for object serialization.
- NSCoding: encodeWithCoder, initWithCoder
- NSKeyedArchiver: Archive to data
- NSKeyedUnarchiver: Unarchive from data
- File storage: Save to file
// Archiving and Serialization in Objective-C
#import <Foundation/Foundation.h>
// Class that supports archiving
@interface Person : NSObject <NSCoding>
@property (nonatomic, strong) NSString *name;
@property (nonatomic, assign) NSInteger age;
- (instancetype)initWithName:(NSString *)name age:(NSInteger)age;
@end
@implementation Person
- (instancetype)initWithName:(NSString *)name age:(NSInteger)age {
self = [super init];
if (self) {
_name = name;
_age = age;
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)coder {
[coder encodeObject:self.name forKey:@"name"];
[coder encodeInteger:self.age forKey:@"age"];
}
- (instancetype)initWithCoder:(NSCoder *)coder {
self = [super init];
if (self) {
_name = [coder decodeObjectForKey:@"name"];
_age = [coder decodeIntegerForKey:@"age"];
}
return self;
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
Person *person = [[Person alloc] initWithName:@"Alice" age:25];
// Archive to NSData
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:person
requiringSecureCoding:NO
error:nil];
NSLog(@"Archived data size: %lu", (unsigned long)data.length);
// Unarchive from NSData
Person *unarchived = [NSKeyedUnarchiver unarchivedObjectOfClass:[Person class]
fromData:data
error:nil];
NSLog(@"Unarchived: %@, %ld", unarchived.name, (long)unarchived.age);
// Save to file
NSString *path = [NSTemporaryDirectory() stringByAppendingPathComponent:@"person.dat"];
[data writeToFile:path atomically:YES];
// Read from file
NSData *fileData = [NSData dataWithContentsOfFile:path];
Person *fromFile = [NSKeyedUnarchiver unarchivedObjectOfClass:[Person class]
fromData:fileData
error:nil];
NSLog(@"From file: %@, %ld", fromFile.name, (long)fromFile.age);
}
return 0;
}JSON serialization using NSJSONSerialization.
- NSJSONSerialization: Convert to/from JSON
- dataWithJSONObject: Dictionary to JSON data
- JSONObjectWithData: JSON data to dictionary
- Options: Pretty printing, reading options
// JSON Serialization in Objective-C
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
@autoreleasepool {
// Create dictionary
NSDictionary *dict = @{
@"name": @"Alice",
@"age": @25,
@"city": @"NYC",
@"hobbies": @[@"reading", @"gaming"]
};
// Convert to JSON data
NSError *error = nil;
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:dict
options:NSJSONWritingPrettyPrinted
error:&error];
if (error) {
NSLog(@"Error: %@", error);
} else {
NSString *jsonString = [[NSString alloc] initWithData:jsonData
encoding:NSUTF8StringEncoding];
NSLog(@"JSON: %@", jsonString);
}
// Parse JSON
NSString *jsonString = @"{"name":"Bob","age":30,"city":"LA"}";
NSData *jsonData2 = [jsonString dataUsingEncoding:NSUTF8StringEncoding];
NSDictionary *parsedDict = [NSJSONSerialization JSONObjectWithData:jsonData2
options:0
error:&error];
if (error) {
NSLog(@"Parse error: %@", error);
} else {
NSLog(@"Parsed: %@", parsedDict);
NSLog(@"Name: %@", parsedDict[@"name"]);
}
// JSON array
NSArray *array = @[@1, @2, @3, @4, @5];
NSData *arrayData = [NSJSONSerialization dataWithJSONObject:array
options:0
error:&error];
if (!error) {
NSString *arrayString = [[NSString alloc] initWithData:arrayData
encoding:NSUTF8StringEncoding];
NSLog(@"Array JSON: %@", arrayString);
}
}
return 0;
}Property list serialization using NSPropertyListSerialization.
- NSPropertyListSerialization: Convert to/from plist
- dataWithPropertyList: Dictionary to plist data
- propertyListWithData: Plist data to dictionary
- Formats: XML, binary
// Property List Serialization in Objective-C
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
@autoreleasepool {
// Create dictionary
NSDictionary *dict = @{
@"name": @"Alice",
@"age": @25,
@"city": @"NYC"
};
// Convert to property list data
NSError *error = nil;
NSData *plistData = [NSPropertyListSerialization dataWithPropertyList:dict
format:NSPropertyListXMLFormat_v1_0
options:0
error:&error];
if (error) {
NSLog(@"Error: %@", error);
} else {
NSString *plistString = [[NSString alloc] initWithData:plistData
encoding:NSUTF8StringEncoding];
NSLog(@"PList: %@", plistString);
}
// Parse property list
NSData *plistData2 = [@"<dict><key>name</key><string>Bob</string></dict>"
dataUsingEncoding:NSUTF8StringEncoding];
NSDictionary *parsedDict = [NSPropertyListSerialization propertyListWithData:plistData2
options:NSPropertyListImmutable
format:NULL
error:&error];
if (error) {
NSLog(@"Parse error: %@", error);
} else {
NSLog(@"Parsed: %@", parsedDict);
}
// Save to file
NSString *path = [NSTemporaryDirectory() stringByAppendingPathComponent:@"data.plist"];
[plistData writeToFile:path atomically:YES];
// Read from file
NSData *fileData = [NSData dataWithContentsOfFile:path];
NSDictionary *fromFile = [NSPropertyListSerialization propertyListWithData:fileData
options:NSPropertyListImmutable
format:NULL
error:&error];
if (!error) {
NSLog(@"From file: %@", fromFile);
}
}
return 0;
}Threading using NSThread, GCD, and NSOperationQueue.
- NSThread: Create and manage threads
- GCD: dispatch_async, dispatch_queue
- NSOperationQueue: Operation-based concurrency
- Thread safety: Use locks or serial queues
// Threading in Objective-C
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
@autoreleasepool {
// Using NSThread
[NSThread detachNewThreadSelector:@selector(threadMethod) toTarget:self withObject:nil];
// Using GCD
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{
[NSThread sleepForTimeInterval:1.0];
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"Main thread callback");
});
});
// Using NSOperationQueue
NSOperationQueue *operationQueue = [[NSOperationQueue alloc] init];
[operationQueue addOperationWithBlock:^{
NSLog(@"Operation executed");
}];
// Thread-safe counter
__block NSInteger counter = 0;
dispatch_queue_t serialQueue = dispatch_queue_create("com.example.serial", DISPATCH_QUEUE_SERIAL);
for (int i = 0; i < 1000; i++) {
dispatch_async(serialQueue, ^{
counter++;
});
}
// Wait for completion
dispatch_barrier_sync(serialQueue, ^{
NSLog(@"Counter: %ld", (long)counter);
});
// Keep main thread alive
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:2.0]];
}
return 0;
}
- (void)threadMethod {
NSLog(@"Thread method executed");
}Filtering using NSPredicate with format strings.
- NSPredicate: Filter arrays, sets
- Format:
@"SELF > 5" - Compound: AND, OR conditions
- Key paths:
@"name CONTAINS 'li'"
// NSPredicate for filtering in Objective-C
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSArray *numbers = @[@1, @2, @3, @4, @5, @6, @7, @8, @9, @10];
// Filter even numbers
NSPredicate *evenPredicate = [NSPredicate predicateWithFormat:@"SELF %% 2 == 0"];
NSArray *evens = [numbers filteredArrayUsingPredicate:evenPredicate];
NSLog(@"Evens: %@", evens);
// Filter greater than 5
NSPredicate *greaterPredicate = [NSPredicate predicateWithFormat:@"SELF > 5"];
NSArray *greater = [numbers filteredArrayUsingPredicate:greaterPredicate];
NSLog(@"Greater than 5: %@", greater);
// Compound predicate
NSPredicate *compoundPredicate = [NSCompoundPredicate andPredicateWithSubpredicates:@[
[NSPredicate predicateWithFormat:@"SELF > 3"],
[NSPredicate predicateWithFormat:@"SELF < 8"]
]];
NSArray *compound = [numbers filteredArrayUsingPredicate:compoundPredicate];
NSLog(@"Between 3 and 8: %@", compound);
// Filter array of dictionaries
NSArray *people = @[
@{@"name": @"Alice", @"age": @25},
@{@"name": @"Bob", @"age": @30},
@{@"name": @"Charlie", @"age": @35}
];
NSPredicate *agePredicate = [NSPredicate predicateWithFormat:@"age > 28"];
NSArray *filteredPeople = [people filteredArrayUsingPredicate:agePredicate];
NSLog(@"People over 28: %@", filteredPeople);
// Filter with contains
NSPredicate *namePredicate = [NSPredicate predicateWithFormat:@"name CONTAINS 'li'"];
NSArray *nameFiltered = [people filteredArrayUsingPredicate:namePredicate];
NSLog(@"Names containing 'li': %@", nameFiltered);
}
return 0;
}Sorting using NSSortDescriptor for key-based sorting.
- NSSortDescriptor: Define sort criteria
- Key: Property to sort by
- Ascending: YES/NO
- Multiple descriptors: Sort by multiple keys
// Sorting with NSSortDescriptor in Objective-C
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSArray *people = @[
@{@"name": @"Alice", @"age": @25, @"city": @"NYC"},
@{@"name": @"Bob", @"age": @30, @"city": @"LA"},
@{@"name": @"Charlie", @"age": @20, @"city": @"Chicago"},
@{@"name": @"David", @"age": @35, @"city": @"NYC"}
];
// Sort by age ascending
NSSortDescriptor *ageDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"age"
ascending:YES];
NSArray *sortedByAge = [people sortedArrayUsingDescriptors:@[ageDescriptor]];
NSLog(@"Sorted by age: %@", sortedByAge);
// Sort by age descending
NSSortDescriptor *ageDescDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"age"
ascending:NO];
NSArray *sortedByAgeDesc = [people sortedArrayUsingDescriptors:@[ageDescDescriptor]];
NSLog(@"Sorted by age descending: %@", sortedByAgeDesc);
// Sort by multiple keys
NSSortDescriptor *cityDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"city"
ascending:YES];
NSSortDescriptor *ageDescriptor2 = [NSSortDescriptor sortDescriptorWithKey:@"age"
ascending:NO];
NSArray *sortedMulti = [people sortedArrayUsingDescriptors:@[cityDescriptor, ageDescriptor2]];
NSLog(@"Sorted by city then age: %@", sortedMulti);
// Sort with comparator
NSArray *sortedWithComparator = [people sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) {
NSNumber *age1 = obj1[@"age"];
NSNumber *age2 = obj2[@"age"];
if ([age1 integerValue] < [age2 integerValue]) {
return NSOrderedAscending;
} else if ([age1 integerValue] > [age2 integerValue]) {
return NSOrderedDescending;
}
return NSOrderedSame;
}];
NSLog(@"Sorted with comparator: %@", sortedWithComparator);
// Sort using valueForKeyPath
NSArray *sortedByKeyPath = [people sortedArrayUsingDescriptors:@[
[NSSortDescriptor sortDescriptorWithKey:@"age" ascending:YES]
]];
NSLog(@"Sorted by key path: %@", sortedByKeyPath);
}
return 0;
}Advanced Key-Value Coding with collection operators.
- Collection operators: @sum, @avg, @max, @min
- Key paths:
employees.@sum.salary - Distinct values: @distinctUnionOfObjects
- Validation: validateValue:forKey:
// KVC (Key-Value Coding) advanced in Objective-C
#import <Foundation/Foundation.h>
// Person class
@interface Person : NSObject
@property (nonatomic, strong) NSString *name;
@property (nonatomic, assign) NSInteger age;
@property (nonatomic, assign) double salary;
@end
@implementation Person
@end
// Department class
@interface Department : NSObject
@property (nonatomic, strong) NSString *name;
@property (nonatomic, strong) NSArray *employees;
@end
@implementation Department
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
// Create employees
Person *p1 = [[Person alloc] init];
[p1 setValue:@"Alice" forKey:@"name"];
[p1 setValue:@25 forKey:@"age"];
[p1 setValue:@50000 forKey:@"salary"];
Person *p2 = [[Person alloc] init];
[p2 setValue:@"Bob" forKey:@"name"];
[p2 setValue:@30 forKey:@"age"];
[p2 setValue:@60000 forKey:@"salary"];
Person *p3 = [[Person alloc] init];
[p3 setValue:@"Charlie" forKey:@"name"];
[p3 setValue:@35 forKey:@"age"];
[p3 setValue:@70000 forKey:@"salary"];
// Create department
Department *dept = [[Department alloc] init];
[dept setValue:@"Engineering" forKey:@"name"];
[dept setValue:@[p1, p2, p3] forKey:@"employees"];
// KVC collection operators
NSNumber *totalSalary = [dept valueForKeyPath:@"employees.@sum.salary"];
NSNumber *avgSalary = [dept valueForKeyPath:@"employees.@avg.salary"];
NSNumber *maxSalary = [dept valueForKeyPath:@"employees.@max.salary"];
NSNumber *minSalary = [dept valueForKeyPath:@"employees.@min.salary"];
NSNumber *count = [dept valueForKeyPath:@"employees.@count"];
NSLog(@"Total salary: %@", totalSalary);
NSLog(@"Average salary: %@", avgSalary);
NSLog(@"Max salary: %@", maxSalary);
NSLog(@"Min salary: %@", minSalary);
NSLog(@"Count: %@", count);
// Get array of values
NSArray *names = [dept valueForKeyPath:@"employees.name"];
NSLog(@"Names: %@", names);
// Distinct values
NSArray *uniqueAges = [dept valueForKeyPath:@"employees.@distinctUnionOfObjects.age"];
NSLog(@"Unique ages: %@", uniqueAges);
// Validate value
NSError *error = nil;
BOOL valid = [dept validateValue:&error forKey:@"name"];
NSLog(@"Valid: %d", valid);
}
return 0;
}Advanced Key-Value Observing with manual notifications.
- Automatic notifications: willChange/didChangeValueForKey:
- Options: NSKeyValueObservingOptionNew/Old
- Context: Pass context for identification
- Remove observer: Avoid crashes on dealloc
// KVO (Key-Value Observing) advanced in Objective-C
#import <Foundation/Foundation.h>
// Observable class
@interface User : NSObject
@property (nonatomic, strong) NSString *name;
@property (nonatomic, assign) NSInteger age;
@end
@implementation User
@end
// Observer class
@interface UserObserver : NSObject
- (void)startObservingUser:(User *)user;
- (void)stopObservingUser:(User *)user;
@end
@implementation UserObserver
- (void)startObservingUser:(User *)user {
[user addObserver:self
forKeyPath:@"name"
options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld
context:NULL];
[user addObserver:self
forKeyPath:@"age"
options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld
context:NULL];
}
- (void)stopObservingUser:(User *)user {
[user removeObserver:self forKeyPath:@"name"];
[user removeObserver:self forKeyPath:@"age"];
}
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary<NSKeyValueChangeKey,id> *)change
context:(void *)context {
if ([keyPath isEqualToString:@"name"]) {
NSString *old = change[NSKeyValueChangeOldKey];
NSString *new = change[NSKeyValueChangeNewKey];
NSLog(@"Name changed from %@ to %@", old, new);
} else if ([keyPath isEqualToString:@"age"]) {
NSNumber *old = change[NSKeyValueChangeOldKey];
NSNumber *new = change[NSKeyValueChangeNewKey];
NSLog(@"Age changed from %ld to %ld", (long)[old integerValue], (long)[new integerValue]);
}
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
User *user = [[User alloc] init];
UserObserver *observer = [[UserObserver alloc] init];
[observer startObservingUser:user];
user.name = @"Alice";
user.age = 25;
user.name = @"Bob";
user.age = 30;
[observer stopObservingUser:user];
// KVO with automatic notifications
@autoreleasepool {
User *user2 = [[User alloc] init];
[user2 addObserver:observer
forKeyPath:@"name"
options:NSKeyValueObservingOptionNew
context:NULL];
[user2 willChangeValueForKey:@"name"];
user2.name = @"Charlie";
[user2 didChangeValueForKey:@"name"];
[user2 removeObserver:observer forKeyPath:@"name"];
}
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.5]];
}
return 0;
}Advanced categories with multiple methods and class extensions.
- Categories: Add methods to existing classes
- Class extensions: Private methods
- Method overriding: Can override existing methods
- Associated objects: Add stored properties
// Categories in Objective-C (advanced)
#import <Foundation/Foundation.h>
// NSString category with multiple methods
@interface NSString (AdvancedExtensions)
- (BOOL)isValidEmail;
- (BOOL)isValidPhoneNumber;
- (NSString *)truncateToLength:(NSUInteger)length;
- (NSArray *)words;
@end
@implementation NSString (AdvancedExtensions)
- (BOOL)isValidEmail {
NSString *pattern = @"^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$";
NSPredicate *pred = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", pattern];
return [pred evaluateWithObject:[self uppercaseString]];
}
- (BOOL)isValidPhoneNumber {
NSString *pattern = @"^\d{3}-\d{3}-\d{4}$";
NSPredicate *pred = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", pattern];
return [pred evaluateWithObject:self];
}
- (NSString *)truncateToLength:(NSUInteger)length {
if (self.length <= length) {
return self;
}
return [[self substringToIndex:length] stringByAppendingString:@"..."];
}
- (NSArray *)words {
return [self componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
}
@end
// NSDate category
@interface NSDate (DateExtensions)
- (NSString *)formattedDate;
- (NSString *)timeAgo;
@end
@implementation NSDate (DateExtensions)
- (NSString *)formattedDate {
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
formatter.dateStyle = NSDateFormatterMediumStyle;
formatter.timeStyle = NSDateFormatterShortStyle;
return [formatter stringFromDate:self];
}
- (NSString *)timeAgo {
NSTimeInterval interval = [[NSDate date] timeIntervalSinceDate:self];
if (interval < 60) {
return @"Just now";
} else if (interval < 3600) {
return [NSString stringWithFormat:@"%ld minutes ago", (long)(interval / 60)];
} else if (interval < 86400) {
return [NSString stringWithFormat:@"%ld hours ago", (long)(interval / 3600)];
} else {
return [NSString stringWithFormat:@"%ld days ago", (long)(interval / 86400)];
}
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSString *email = @"test@example.com";
NSLog(@"Is valid email: %d", [email isValidEmail]);
NSString *phone = @"123-456-7890";
NSLog(@"Is valid phone: %d", [phone isValidPhoneNumber]);
NSString *longStr = @"This is a very long string that needs truncation";
NSLog(@"Truncated: %@", [longStr truncateToLength:20]);
NSString *sentence = @"Hello world from Objective-C";
NSLog(@"Words: %@", [sentence words]);
NSDate *date = [NSDate dateWithTimeIntervalSinceNow:-3600];
NSLog(@"Formatted date: %@", [date formattedDate]);
NSLog(@"Time ago: %@", [date timeAgo]);
}
return 0;
}Prototype pattern using copy and mutableCopy protocols.
- NSCopying: Implement
copyWithZone: - NSMutableCopying: Implement
mutableCopyWithZone: - Shallow copy:
[obj copy] - Deep copy: Custom implementation
- Benefits: Object reuse, performance
// Blocks and Closures in Objective-C
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
@autoreleasepool {
// Basic block
void (^simpleBlock)(void) = ^{
NSLog(@"Simple block executed");
};
simpleBlock();
// Block with parameters
NSString * (^greetingBlock)(NSString *) = ^(NSString *name) {
return [NSString stringWithFormat:@"Hello, %@!", name];
};
NSLog(@"%@", greetingBlock(@"Alice"));
// Block with return value
NSInteger (^addBlock)(NSInteger, NSInteger) = ^NSInteger(NSInteger a, NSInteger b) {
return a + b;
};
NSLog(@"%ld", (long)addBlock(5, 3));
// Block capturing variables
NSInteger multiplier = 2;
NSInteger (^multiplyBlock)(NSInteger) = ^NSInteger(NSInteger x) {
return x * multiplier;
};
NSLog(@"%ld", (long)multiplyBlock(5)); // 10
// Block with __block variable (modifiable)
__block NSInteger counter = 0;
void (^incrementBlock)(void) = ^{
counter++;
};
incrementBlock();
incrementBlock();
NSLog(@"Counter: %ld", (long)counter); // 2
// Block as completion handler
void (^completionHandler)(id, NSError *) = ^(id result, NSError *error) {
if (error) {
NSLog(@"Error: %@", error);
} else {
NSLog(@"Result: %@", result);
}
};
completionHandler(@"Success", nil);
// Block in array
NSArray *blocks = @[
^(void) { NSLog(@"Block 1"); },
^(void) { NSLog(@"Block 2"); },
^(void) { NSLog(@"Block 3"); }
];
for (void (^block)(void) in blocks) {
block();
}
// Block returning block
NSInteger (^getMultiplier(NSInteger factor))(NSInteger) = ^(NSInteger factor) {
return ^NSInteger(NSInteger x) {
return x * factor;
};
};
NSInteger (^doubleBlock)(NSInteger) = getMultiplier(2);
NSLog(@"%ld", (long)doubleBlock(5)); // 10
// Block with typedef
typedef void (^LogBlock)(NSString *);
LogBlock logBlock = ^(NSString *message) {
NSLog(@"Log: %@", message);
};
logBlock(@"Hello");
// Block with GCD
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[NSThread sleepForTimeInterval:0.5];
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"Main thread callback");
});
});
// Keep main thread alive
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:1.0]];
}
return 0;
}