InterviewPitch
Objective-C interview questions

Objective-C Interview Questions with Answers

Most Asked Objective-C Interview Questions for Apple Platform Roles

100+ QuestionsDetailed AnswersCode ExamplesUpdated for 2026

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

Beginner
1. What is Objective-C?

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++
objective-c
// Hello World in Objective-C
#import <Foundation/Foundation.h>

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSLog(@"Hello, World!");
    }
    return 0;
}
Beginner
2. How to declare variables in Objective-C?

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: static keyword
  • Constants: const keyword
objective-c
// 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;
}
Beginner
3. What are the data types in Objective-C?

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
objective-c
// 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;
}
Beginner
4. How to define functions in Objective-C?

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
objective-c
// 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, &quotient, &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;
}
Beginner
5. What are arrays in Objective-C?

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
objective-c
// 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;
}
Beginner
6. What are collections in Objective-C?

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
objective-c
// 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;
}
Beginner
7. What are data classes in Objective-C?

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 NSCopying protocol
  • Description: Override description method
objective-c
// 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;
}
Beginner
8. What are sealed classes in Objective-C?

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
objective-c
// 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;
}
Beginner
9. What is null safety in Objective-C?

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
objective-c
// 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;
}
Beginner
10. What are control flow statements in Objective-C?

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) { }
objective-c
// 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;
}
Beginner
11. What are classes and inheritance in Objective-C?

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
objective-c
// 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;
}
Beginner
12. What are properties in Objective-C?

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: readonly attribute
  • Synthesize: @synthesize (modern compilers auto-synthesize)
objective-c
// 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;
}
Intermediate
13. What are class methods in Objective-C?

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: sharedInstance method
  • Factory methods: + (instancetype)create
  • Class variables: Static variables
  • dispatch_once: Thread-safe initialization
objective-c
// 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;
}
Intermediate
14. How to handle exceptions in Objective-C?

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
objective-c
// 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;
}
Intermediate
15. What are blocks in Objective-C?

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
objective-c
// 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);
}
Intermediate
16. What are categories in Objective-C?

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 ()
objective-c
// 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;
}
Intermediate
17. What are protocols in Objective-C?

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
objective-c
// 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;
}
Intermediate
18. What are type aliases in Objective-C?

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)
objective-c
// 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;
}
Intermediate
19. What are inline functions in Objective-C?

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
objective-c
// 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;
}
Intermediate
20. What are higher-order functions in Objective-C?

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_async with blocks
  • Composition: Combining blocks
objective-c
// 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;
}
Advanced
21. What is Grand Central Dispatch (GCD) in Objective-C?

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
objective-c
// 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;
}
Advanced
22. What is NSNotificationCenter in Objective-C?

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
objective-c
// 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;
}
Advanced
23. What is Key-Value Observing (KVO) in Objective-C?

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:
objective-c
// 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;
}
Advanced
24. What is Key-Value Coding (KVC) in Objective-C?

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:
objective-c
// 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;
}
Advanced
25. What are generics in Objective-C?

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
objective-c
// 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;
}
Advanced
26. What is delegation in Objective-C?

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
objective-c
// 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;
}
Advanced
27. What is the singleton pattern in Objective-C?

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
objective-c
// 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;
}
Advanced
28. What is the factory pattern in Objective-C?

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
objective-c
// 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;
}
Advanced
29. What is the strategy pattern in Objective-C?

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
objective-c
// 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;
}
Advanced
30. What is the Observer Pattern in Objective-C?

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
objective-c
// 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;
}
Coding Round
31. Reverse a string

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
objective-c
// 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;
}
Coding Round
32. Check palindrome

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
objective-c
// 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;
}
Coding Round
33. Find max in array

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
objective-c
// 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;
}
Coding Round
34. Remove duplicates

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
objective-c
// 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;
}
Coding Round
35. Merge arrays

Merge arrays using arrayByAddingObjectsFromArray or NSMutableArray.

  • NSArray: [array1 arrayByAddingObjectsFromArray:array2]
  • NSMutableArray: [mutableArray addObjectsFromArray:array2]
  • Unique merge: Use NSSet
  • Complexity: O(n) time
objective-c
// 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;
}
Coding Round
36. Convert string to number

Convert string to number using integerValue, doubleValue, or NSNumberFormatter.

  • integerValue: [str integerValue]
  • doubleValue: [str doubleValue]
  • NSNumberFormatter: Safe conversion
  • NSScanner: Parse with validation
objective-c
// 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;
}
Coding Round
37. Loop through dictionary

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
objective-c
// 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;
}
Coding Round
38. Delay function execution

Delay execution using GCD, NSTimer, or performSelector.

  • performSelector: performSelector:withObject:afterDelay:
  • GCD: dispatch_after
  • NSTimer: scheduledTimerWithTimeInterval
  • NSThread: sleepForTimeInterval
objective-c
// 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)");
}
Coding Round
39. HTTP GET request

Make HTTP GET requests using NSURLSession or NSURLConnection.

  • NSURLSession: dataTaskWithURL:completionHandler:
  • NSURLConnection: sendSynchronousRequest
  • Headers: setValue:forHTTPHeaderField:
  • Error handling: Check NSError
objective-c
// 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;
}
Coding Round
40. Create a promise-like Deferred

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
objective-c
// 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;
}
Coding Round
41. Factorial

Calculate factorial using recursion or iteration.

  • Recursive: n * factorial(n-1)
  • Iterative: Loop with multiplication
  • Base case: n <= 1
  • Edge cases: 0! = 1
objective-c
// 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;
}
Coding Round
42. Fibonacci

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
objective-c
// 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;
}
Coding Round
43. FizzBuzz

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
objective-c
// 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;
}
Coding Round
44. Find missing number

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
objective-c
// 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;
}
Coding Round
45. Find duplicates

Find duplicates using NSSet or NSCountedSet.

  • NSSet: Track seen elements
  • NSCountedSet: Count occurrences
  • Return: Elements with count > 1
  • Complexity: O(n) time
objective-c
// 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;
}
Coding Round
46. Sum of array

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
objective-c
// 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;
}
Coding Round
47. Average of array

Calculate average using @avg operator or manual division.

  • KVC: [array valueForKeyPath:@"@avg.self"]
  • Manual: sum / count
  • Empty array: Returns 0
  • Precision: Returns double
objective-c
// 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;
}
Coding Round
48. Sort array ascending

Sort using sortedArrayUsingSelector or sortedArrayUsingComparator.

  • Selector: sortedArrayUsingSelector:@selector(compare:)
  • Comparator: sortedArrayUsingComparator:^NSComparisonResult
  • Sort descriptors: sortedArrayUsingDescriptors:
  • In-place: sortUsingSelector:
objective-c
// 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;
}
Coding Round
49. Sort array descending

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)
objective-c
// 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;
}
Coding Round
50. Flatten nested array

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
objective-c
// 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;
}
Coding Round
51. Chunk array

Split array into chunks using subarrayWithRange.

  • Loop: Iterate with step size
  • subarrayWithRange: Extract chunks
  • Edge case: Handle last chunk
  • Complexity: O(n) time
objective-c
// 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;
}
Coding Round
53. Quick sort

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
objective-c
// 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;
}
Coding Round
54. Merge sort

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
objective-c
// 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;
}
Coding Round
55. Bubble sort

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
objective-c
// 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;
}
Coding Round
56. Intersection of arrays

Find common elements using NSSet or predicate.

  • NSSet: [setA intersectsSet:setB]
  • Predicate: filteredArrayUsingPredicate:
  • Manual: Check if in set
  • Complexity: O(n) time with set
objective-c
// 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;
}
Coding Round
57. Union of arrays

Combine arrays with unique elements using NSSet or NSOrderedSet.

  • NSSet: [setA unionSet:setB]
  • NSOrderedSet: Preserves order
  • Manual: Add unique elements
  • Complexity: O(n) time
objective-c
// 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;
}
Coding Round
58. Difference of arrays

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
objective-c
// 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;
}
Coding Round
59. Group by property

Group objects by property using NSMutableDictionary.

  • Dictionary: Group by key
  • Key-Value: Use property value as key
  • Filtering: Use NSPredicate
  • Complexity: O(n) time
objective-c
// 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;
}
Coding Round
60. Deep clone object

Deep clone using NSCopying protocol and recursive copying.

  • NSCopying: Implement copyWithZone:
  • Recursive: Copy nested objects
  • NSObject: copy method
  • Mutable copy: mutableCopy
objective-c
// 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;
}
Coding Round
61. Immutable update

Perform immutable updates using mutableCopy and recursive updates.

  • MutableCopy: Create mutable copy
  • Recursive: Update nested structures
  • Path: Use dot notation
  • Return: New immutable dictionary
objective-c
// 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;
}
Coding Round
62. Pipe function

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
objective-c
// 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;
}
Coding Round
63. Compose function

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
objective-c
// 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;
}
Coding Round
64. Memoization

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
objective-c
// 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;
}
Coding Round
65. Once function

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
objective-c
// 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;
}
Coding Round
66. Debounce with leading edge

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
objective-c
// 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;
}
Coding Round
67. Throttle with leading edge

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
objective-c
// 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;
}
Coding Round
68. Deep equal

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
objective-c
// 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;
}
Coding Round
69. Observable pattern

Observable pattern using NSNotificationCenter or custom implementation.

  • Observable: Maintains subscribers
  • Subscribe: Add callback
  • Notify: Call all subscribers
  • Unsubscribe: Remove callback
objective-c
// 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;
}
Coding Round
70. Singleton pattern

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
objective-c
// 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;
}
Coding Round
71. Factory pattern

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
objective-c
// 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;
}
Coding Round
72. Strategy pattern

Strategy pattern using protocols and composition.

  • Strategy protocol: Defines algorithm interface
  • Context: Uses strategy
  • Runtime switching: Change strategy at runtime
  • Benefits: Encapsulate algorithms
objective-c
// 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;
}
Coding Round
73. Observer pattern

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
objective-c
// 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;
}
Coding Round
74. Decorator pattern

Decorator pattern using wrapper functions or classes.

  • Component: Base object
  • Decorator: Wraps component
  • Chaining: Multiple decorators
  • Benefits: Add behavior dynamically
objective-c
// 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;
}
Coding Round
75. Command pattern

Command pattern with execute and undo methods.

  • Command protocol: Execute and undo methods
  • Receiver: Performs actual work
  • Invoker: Executes commands
  • Undo/Redo: Command history
objective-c
// 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;
}
Coding Round
76. Memento pattern

Memento pattern for state capture and restoration.

  • Originator: Creates and restores mementos
  • Memento: Stores state
  • Caretaker: Manages mementos
  • Undo/Redo: State history
objective-c
// 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;
}
Coding Round
77. Mediator pattern

Mediator pattern for centralized communication.

  • Mediator: Encapsulates communication
  • Colleague: Communicates through mediator
  • Benefits: Loose coupling
  • Use case: Chat systems
objective-c
// 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;
}
Coding Round
78. Chain of Responsibility

Chain of Responsibility for processing requests sequentially.

  • Handler: Processes or forwards
  • Chain: Linked list of handlers
  • Benefits: Decoupling
  • Use case: Logging, authentication
objective-c
// 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;
}
Coding Round
79. State pattern

State pattern for changing behavior with state.

  • Context: Maintains state
  • State: Defines behavior
  • Transitions: Change between states
  • Benefits: Clean state management
objective-c
// 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;
}
Coding Round
80. Proxy pattern

Proxy pattern for controlling access to objects.

  • Subject: Real object
  • Proxy: Controls access
  • Lazy loading: Create on demand
  • Benefits: Access control, logging
objective-c
// 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;
}
Coding Round
81. Flyweight pattern

Flyweight pattern for sharing objects to save memory.

  • Flyweight: Shared object
  • Factory: Manages flyweights
  • Benefits: Memory optimization
  • Use case: Character rendering
objective-c
// 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;
}
Coding Round
82. Bridge pattern

Bridge pattern for separating abstraction from implementation.

  • Abstraction: High-level interface
  • Implementation: Low-level operations
  • Benefits: Separation of concerns
  • Use case: Cross-platform
objective-c
// 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;
}
Coding Round
83. Adapter pattern

Adapter pattern for converting interfaces.

  • Target: Expected interface
  • Adaptee: Existing interface
  • Adapter: Bridges interfaces
  • Benefits: Reusability
objective-c
// 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;
}
Coding Round
84. Facade pattern

Facade pattern for simplifying complex subsystems.

  • Facade: Simplified interface
  • Subsystem: Complex components
  • Benefits: Simplified interface
  • Use case: Library APIs
objective-c
// 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;
}
Coding Round
85. Composite pattern

Composite pattern for tree structures.

  • Component: Interface for all
  • Leaf: Individual object
  • Composite: Container
  • Benefits: Uniform interface
objective-c
// 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;
}
Coding Round
86. Visitor pattern

Visitor pattern for adding operations without modifying elements.

  • Visitor: Defines operations
  • Element: Accepts visitors
  • Benefits: Adding operations without modifying
  • Use case: Compilers, AST
objective-c
// 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;
}
Coding Round
87. Iterator pattern

Iterator pattern for sequential access to collections.

  • Iterator: Traverses collection
  • Aggregate: Creates iterator
  • Benefits: Uniform traversal
  • Use case: Collection traversal
objective-c
// 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;
}
Coding Round
88. Template Method pattern

Template Method for algorithm skeletons.

  • AbstractClass: Defines template
  • ConcreteClass: Implements steps
  • Benefits: Code reuse
  • Use case: Frameworks
objective-c
// 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;
}
Coding Round
89. Builder pattern

Builder pattern for constructing complex objects.

  • Builder: Constructs parts
  • Director: Orchestrates construction
  • Product: Constructed object
  • Benefits: Step-by-step construction
objective-c
// 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;
}
Coding Round
90. Prototype pattern

Prototype pattern for cloning objects using NSCopying.

  • NSCopying: Implement copyWithZone:
  • Shallow copy: copy
  • Deep copy: Recursive copy
  • Benefits: Object reuse, performance
objective-c
// 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;
}
Coding Round
91. Archiving and Serialization

Archiving using NSCoding protocol for object serialization.

  • NSCoding: encodeWithCoder, initWithCoder
  • NSKeyedArchiver: Archive to data
  • NSKeyedUnarchiver: Unarchive from data
  • File storage: Save to file
objective-c
// 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;
}
Coding Round
92. JSON Serialization

JSON serialization using NSJSONSerialization.

  • NSJSONSerialization: Convert to/from JSON
  • dataWithJSONObject: Dictionary to JSON data
  • JSONObjectWithData: JSON data to dictionary
  • Options: Pretty printing, reading options
objective-c
// 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;
}
Coding Round
93. Property List Serialization

Property list serialization using NSPropertyListSerialization.

  • NSPropertyListSerialization: Convert to/from plist
  • dataWithPropertyList: Dictionary to plist data
  • propertyListWithData: Plist data to dictionary
  • Formats: XML, binary
objective-c
// 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;
}
Coding Round
94. Threading

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
objective-c
// 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");
}
Coding Round
95. NSPredicate for filtering

Filtering using NSPredicate with format strings.

  • NSPredicate: Filter arrays, sets
  • Format: @"SELF > 5"
  • Compound: AND, OR conditions
  • Key paths: @"name CONTAINS 'li'"
objective-c
// 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;
}
Coding Round
96. Sorting with NSSortDescriptor

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
objective-c
// 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;
}
Coding Round
97. KVC Advanced

Advanced Key-Value Coding with collection operators.

  • Collection operators: @sum, @avg, @max, @min
  • Key paths: employees.@sum.salary
  • Distinct values: @distinctUnionOfObjects
  • Validation: validateValue:forKey:
objective-c
// 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;
}
Coding Round
98. KVO Advanced

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
objective-c
// 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;
}
Coding Round
99. Categories Advanced

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
objective-c
// 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;
}
Coding Round
100. Prototype pattern

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
objective-c
// 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;
}