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 crashProperty 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