Objective-C
01 / 02

Syntax, Properties & Memory Basics

Objective-C: Syntax, Properties & Memory Basics

Interface, Implementation & Messaging

// Person.h — public interface
#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface Person : NSObject

@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) NSInteger age;
@property (nonatomic, strong, nullable) Person *bestFriend;

- (instancetype)initWithName:(NSString *)name age:(NSInteger)age NS_DESIGNATED_INITIALIZER;
- (NSString *)greeting;

@end

NS_ASSUME_NONNULL_END

// Person.m — implementation
#import "Person.h"

@implementation Person

- (instancetype)initWithName:(NSString *)name age:(NSInteger)age {
    self = [super init];
    if (self) {
        _name = [name copy];
        _age = age;
    }
    return self;
}

- (NSString *)greeting {
    return [NSString stringWithFormat:@"Hi, I'm %@ (%ld)", self.name, (long)self.age];
}

@end

// Message-send syntax: [receiver message:arg]
Person *p = [[Person alloc] initWithName:@"Alice" age:30];
NSLog(@"%@", [p greeting]);

// Nested messages read right-to-left
NSString *upper = [[p name] uppercaseString];

// nil is safe to message — returns zero/nil, never crashes
Person *missing = nil;
[missing greeting]; // returns nil, no crash

Property Attributes

// strong — retains the object, keeps it alive
@property (nonatomic, strong) NSArray<NSString *> *tags;

// weak — does not retain; auto-nils when the referent deallocates.
// Standard for delegate back-references to avoid retain cycles.
@property (nonatomic, weak) id<PersonDelegate> delegate;

// copy — takes an immutable snapshot at assignment time.
// Preferred for NSString/NSArray/NSDictionary so a caller's later
// mutation of an NSMutableString they passed in can't change your value.
@property (nonatomic, copy) NSString *identifier;

// assign — plain value, no retain (primitives, or non-object C types)
@property (nonatomic, assign) CGFloat opacity;

// atomic (default) vs nonatomic:
// atomic only guarantees a single getter/setter call isn't interrupted
// mid-write by another thread — it does NOT make read-modify-write
// sequences thread-safe. Cocoa code almost always uses nonatomic and
// handles real thread-safety explicitly (locks, GCD queues).
@property (atomic, strong) NSNumber *counter; // rare — usually nonatomic

// readonly publicly, readwrite privately — via a class extension
@property (nonatomic, copy, readonly) NSString *name;

ARC: Ownership & Retain Cycles

// ARC inserts retain/release calls at compile time based on
// static ownership analysis — there is no runtime GC pass.

// Retain cycle: two objects strongly reference each other
@interface Parent : NSObject
@property (nonatomic, strong) Child *child;
@end

@interface Child : NSObject
@property (nonatomic, strong) Parent *parent; // BUG: should be weak
@end
// Neither object's retain count ever reaches zero → leak

// Fix: back-reference is weak
@property (nonatomic, weak) Parent *parent;

// Blocks capture self strongly by default — a block stored on self
// creates the same cycle: self -> block -> self
@property (nonatomic, copy) void (^completion)(void);

- (void)loadData {
    __weak typeof(self) weakSelf = self;
    self.completion = ^{
        __strong typeof(weakSelf) strongSelf = weakSelf;
        if (!strongSelf) return; // self was already deallocated
        [strongSelf handleResult];
    };
}

// dealloc — no [super dealloc] call needed under ARC (unlike MRC)
- (void)dealloc {
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}

Foundation Collections & Blocks

// NSArray / NSMutableArray
NSArray<NSString *> *names = @[@"Alice", @"Bob", @"Carol"];
NSMutableArray<NSString *> *mutableNames = [names mutableCopy];
[mutableNames addObject:@"Dave"];
[mutableNames removeObjectAtIndex:0];

// NSDictionary / NSMutableDictionary
NSDictionary<NSString *, NSNumber *> *scores = @{@"Alice": @95, @"Bob": @82};
NSNumber *aliceScore = scores[@"Alice"]; // subscript syntax

// Fast enumeration
for (NSString *name in names) {
    NSLog(@"%@", name);
}

// Block-based enumeration (with index and stop)
[names enumerateObjectsUsingBlock:^(NSString *name, NSUInteger idx, BOOL *stop) {
    if ([name isEqualToString:@"Bob"]) {
        *stop = YES; // early exit
    }
}];

// Blocks as first-class closures — capture surrounding variables
NSInteger threshold = 90;
NSArray<NSString *> *topScorers = [scores.allKeys filteredArrayUsingPredicate:
    [NSPredicate predicateWithBlock:^BOOL(NSString *key, NSDictionary *bindings) {
        return [scores[key] integerValue] >= threshold;
    }]];

typedef void (^CompletionBlock)(BOOL success, NSError * _Nullable error);

- (void)fetchWithCompletion:(CompletionBlock)completion {
    completion(YES, nil);
}

Keep your own version of these notes — editable, searchable, and organised by your stack.

Start free