Cheatsheet Objective-C
Linguagem orientada a objetos da Apple para macOS e iOS
Objective-C
Basic
Primitive Types
int age = 30; float pi = 3.14f; double precise = 3.14159265; BOOL active = YES; // or NO char c = 'A'; long big = 1000000L; // Platform-safe Foundation types: NSInteger count = 42; CGFloat x = 3.14;
Objective-C uses the C types (int, float, double, char) plus BOOL (YES/NO). Prefer NSInteger and CGFloat because they are safe across platforms.
Modern Literals
// NSString:
NSString *name = @"Anna";
// NSNumber:
NSNumber *n = @42;
NSNumber *f = @3.14;
NSNumber *b = @YES;
// NSArray:
NSArray *arr = @[@"a", @"b", @"c"];
// NSDictionary:
NSDictionary *dict = @{
@"name": @"Anna",
@"age": @30
};Modern literals use @ to create objects concisely: @42 (NSNumber), @[] (NSArray) and @{} (NSDictionary), replacing the long alloc/init methods.
Comments and Structure
// Line comment /* Multi-line comment */ /// Documentation comment (HeaderDoc) // Typical file structure: // Person.h -> @interface (declaration) // Person.m -> @implementation (code) #import "Person.h" // imports header
Comments use // and /* */; /// generates documentation. Each class splits into .h (public interface) and .m (implementation), linked by #import.
Message Syntax
// [receiver message]: [array count]; [string length]; // With arguments (labels): [array objectAtIndex:0]; [dict objectForKey:@"key"]; [string substringToIndex:5]; // Nested messages: [[NSString alloc] initWithFormat:@"%d", 42];
Methods are called through messages between square brackets: [receiver message]. Each argument has a descriptive label, making the calls readable.
NSLog and Formatting
NSLog(@"Plain text"); NSLog(@"Age: %d", 30); // int NSLog(@"Name: %@", name); // object NSLog(@"Pi: %.2f", 3.14159); // float NSLog(@"Count: %lu", (unsigned long)n); NSLog(@"Active: %d", active); // BOOL // %@ calls the object -description
NSLog prints to the console with format specifiers: %d (int), %f (float), %@ (object, calls description). It is the basic debugging tool.
Flow Control
if (age >= 18) {
NSLog(@"Adult");
} else {
NSLog(@"Minor");
}
for (int i = 0; i < 10; i++) { }
while (x > 0) { x--; }
// Fast enumeration:
for (NSString *item in array) {
NSLog(@"%@", item);
}The control structures come from C: if/else, for, while. The for...in (fast enumeration) iterates collections in a simple and fast way.
Operators
// Arithmetic: int s = 2 + 3; // 5 int d = 10 / 3; // 3 (integer) int r = 10 % 3; // 1 (remainder) // Comparison: BOOL eq = (5 == 5); // YES BOOL gt = (5 > 3); // YES // Logical: BOOL both = (a && b); // AND BOOL either = (a || b); // OR BOOL notA = !a; // NOT // Increment: i++; ++i; i += 5;
The operators are the C ones: arithmetic (+ - * / %), comparison (== != > <) and logical (&& || !). Compound assignment (+=) and increment (++) are also available.
switch
switch (day) {
case 1:
NSLog(@"Monday");
break;
case 2:
NSLog(@"Tuesday");
break;
default:
NSLog(@"Other");
break;
}
// Ternary:
NSString *r = (x > 0) ? @"pos" : @"neg";The switch compares integer values, with case and default. Each case needs a break to avoid falling through to the next one. The ternary operator ? : is an inline if.
nil Messaging
Person *p = nil;
// Sending a message to nil does not crash:
NSString *name = [p name]; // nil
NSInteger n = [p count]; // 0
BOOL flag = [p isValid]; // NO
// Useful for checks:
if ([name length] > 0) {
// only runs if name is not nil/empty
}Sending a message to nil is safe and does not cause a crash — it returns nil, 0 or NO. This removes many null checks common in other languages.
Objects and Classes
Declaring a Class (@interface)
// Person.h #import <Foundation/Foundation.h> @interface Person : NSObject @property (nonatomic, strong) NSString *name; @property (nonatomic) NSInteger age; - (NSString *)greeting; - (instancetype)initWithName:(NSString *)name; @end
The @interface in the .h file declares the class, its properties and the public methods. Instance methods start with -; the class inherits from NSObject.
super and Override
@implementation Student
// Override a parent method:
- (NSString *)greeting {
NSString *base = [super greeting];
return [base stringByAppendingString:
@" (student)"];
}
@end
// super calls the parent class versionTo override a method, redefine it in the subclass. super calls the parent class implementation, allowing the behavior to be extended instead of fully replaced.
Enumerations (NS_ENUM)
// Define a typed enum:
typedef NS_ENUM(NSInteger, OrderStatus) {
OrderStatusPending,
OrderStatusShipped,
OrderStatusDelivered
};
// Use:
OrderStatus status = OrderStatusShipped;
if (status == OrderStatusShipped) {
NSLog(@"Shipped");
}
// NS_OPTIONS for flags (bitmask)The NS_ENUM macro defines enumerations with an explicit base type (e.g. NSInteger), safer than the C enum. NS_OPTIONS is used for combinable flags (bitmask).
Implementation (@implementation)
// Person.m
#import "Person.h"
@implementation Person
- (NSString *)greeting {
return [NSString stringWithFormat:
@"Hello, %@", self.name];
}
- (instancetype)initWithName:(NSString *)name {
self = [super init];
if (self) {
_name = name;
}
return self;
}
@endThe @implementation in the .m file contains the method code. self refers to the current instance and _name is the instance variable (ivar) generated by the property.
Class Methods (+)
@interface Person : NSObject
// Instance method (-):
- (NSString *)details;
// Class method (+):
+ (instancetype)personWithName:(NSString *)name;
@end
@implementation Person
+ (instancetype)personWithName:(NSString *)name {
Person *p = [[Person alloc] init];
p.name = name;
return p;
}
@end
// Usage: Person *p = [Person personWithName:@"Anna"];Class methods start with + (instance ones with -). They are often used the factory methods (alternative constructors) that return instancetype.
Creating Objects
// alloc + init: Person *p = [[Person alloc] init]; p.name = @"Anna"; p.age = 30; // With custom initializer: Person *p2 = [[Person alloc] initWithName:@"Ray"]; // Use: NSLog(@"%@", [p greeting]); NSLog(@"%ld years old", (long)p.age);
An object is created with alloc (allocates memory) followed by init (initializes). Properties are accessed with a dot (p.name) and methods through messages ([p greeting]).
id and instancetype
// id: pointer to any object id object = @"a string"; object = @42; // now it is NSNumber // No compile-time type checking [object length]; // runs at runtime // instancetype: type of the receiving class + (instancetype)create; // returns the right type // Better than id in initializers/factories
The id type is a pointer to any object, without compile-time type checking. instancetype is preferable in initializers and factories, since it indicates the actual class type.
Inheritance
// Student.h
@interface Student : Person
@property (nonatomic, strong) NSString *school;
- (CGFloat)average;
@end
// Student.m
@implementation Student
- (CGFloat)average {
return 16.5;
}
@endInheritance uses : ParentClass in the @interface. The subclass inherits properties and methods from the parent and can add its own. In Objective-C there is only single inheritance.
description and isEqual
@implementation Person
// Customize printing (NSLog %@):
- (NSString *)description {
return [NSString stringWithFormat:
@"<Person: %@, %ld>", self.name, (long)self.age];
}
// Equality:
- (BOOL)isEqual:(id)other {
return [self.name isEqual:[other name]];
}
- (NSUInteger)hash {
return self.name.hash;
}
@endThe description method defines the text shown by NSLog(@"%@", obj). To compare objects by value, override isEqual: and hash together.
Strings and Collections
NSString
NSString *s = @"Hello World"; [s length]; // 11 [s uppercaseString]; // "HELLO WORLD" [s lowercaseString]; // "hello world" [s substringToIndex:5]; // "Hello" [s containsString:@"World"]; // YES [s stringByAppendingString:@"!"]; [s componentsSeparatedByString:@" "];
NSString is immutable. Methods like uppercaseString, substringToIndex: and containsString: always return a new string, without changing the original.
NSMutableArray
NSMutableArray *m = [NSMutableArray
arrayWithArray:@[@"Anna", @"Ray"]];
[m addObject:@"Joe"];
[m removeObjectAtIndex:1];
[m insertObject:@"X" atIndex:0];
[m replaceObjectAtIndex:0 withObject:@"Y"];
[m sortUsingSelector:@selector(compare:)];NSMutableArray allows modifying the collection: addObject: adds, removeObjectAtIndex: removes, insertObject:atIndex: inserts and sortUsingSelector: sorts.
NSMutableString
NSMutableString *ms = [NSMutableString
stringWithString:@"Hello World"];
[ms appendString:@"!"];
[ms replaceOccurrencesOfString:@"World"
withString:@"ObjC"
options:0
range:NSMakeRange(0, ms.length)];
[ms deleteCharactersInRange:NSMakeRange(0, 6)];NSMutableString is the mutable version: appendString: appends, replaceOccurrencesOfString: replaces and deleteCharactersInRange: removes, changing the string itself.
NSDictionary
NSDictionary *dict = @{
@"name": @"Anna",
@"age": @30
};
[dict objectForKey:@"name"]; // "Anna"
dict[@"name"]; // "Anna" (subscript)
[dict count]; // 2
[dict allKeys]; // all the keys
[dict allValues]; // all the valuesNSDictionary stores immutable key-value pairs. Access uses objectForKey: or subscript dict[@"key"]. Keys must be unique and cannot be nil.
String Formatting
// stringWithFormat:
NSString *s = [NSString stringWithFormat:
@"%@ is %ld years old", name, (long)age];
// With numbers:
NSString *price = [NSString stringWithFormat:
@"%.2f EUR", 19.99];
// Join an array:
NSString *list = [array
componentsJoinedByString:@", "];stringWithFormat: creates formatted strings with specifiers (%@, %ld, %.2f). componentsJoinedByString: joins the elements of an array with a separator.
NSMutableDictionary
NSMutableDictionary *m = [NSMutableDictionary
dictionaryWithDictionary:@{@"name": @"Anna"}];
// Add / update:
m[@"email"] = @"ana@mail.com";
[m setObject:@30 forKey:@"age"];
// Remove:
[m removeObjectForKey:@"age"];
[m removeAllObjects];NSMutableDictionary allows changing the dictionary: assign by subscript (m[@"key"] = value) or with setObject:forKey:, and remove with removeObjectForKey:.
NSArray
NSArray *arr = @[@"Anna", @"Ray", @"Mia"];
[arr count]; // 3
[arr objectAtIndex:0]; // "Anna"
arr[0]; // "Anna" (subscript)
[arr containsObject:@"Ray"]; // YES
[arr firstObject]; // "Anna"
[arr lastObject]; // "Mia"
// Iterate:
for (NSString *name in arr) { }NSArray is an ordered and immutable collection of objects. Access uses objectAtIndex: or the subscript syntax arr[0]. It does not accept nil values.
Collection Enumeration
// Fast enumeration:
for (NSString *item in array) {
NSLog(@"%@", item);
}
for (NSString *key in dict) {
NSLog(@"%@ = %@", key, dict[key]);
}
// Block enumeration:
[array enumerateObjectsUsingBlock:
^(id obj, NSUInteger idx, BOOL *stop) {
NSLog(@"%lu: %@", (unsigned long)idx, obj);
}];The for...in iterates arrays and dictionaries simply. Block enumeration (enumerateObjectsUsingBlock:) gives access to the index and allows stopping with *stop = YES.
Memory and Properties
ARC
// ARC (Automatic Reference Counting): // The compiler inserts retain/release // You do not call [obj release] manually Person *p = [[Person alloc] init]; // p has a strong reference p = nil; // releases the object (no refs) // ARC handles memory at compile-time
ARC (Automatic Reference Counting) manages memory automatically at compile time, inserting retain/release. You never call release manually.
readonly and Custom Getter
@interface Person : NSObject
// Read-only (no public setter):
@property (nonatomic, readonly) NSInteger age;
// Getter with a custom name:
@property (nonatomic, getter=isActive) BOOL active;
@end
// Usage:
if (person.isActive) { } // custom getter
person.age = 30; // ERROR (readonly)The readonly attribute prevents the generation of a public setter. The getter=isActive customizes the getter name, a common convention for boolean properties.
strong and weak
// strong (default): keeps the object alive @property (nonatomic, strong) NSString *name; // weak: does not retain (becomes nil when released) @property (nonatomic, weak) id<Delegate> delegate; // Example: Person * __weak weakRef = person; person = nil; NSLog(@"%@", weakRef); // (null)
A strong reference keeps the object alive; a weak one does not count toward the reference count and automatically becomes nil when the object is released, avoiding cycles.
Retain Cycles
// Problem: A strong -> B and B strong -> A // Neither is released (memory leak) // Solution: one weak reference @interface Child : NSObject @property (nonatomic, weak) Parent *parent; // weak! @end @interface Parent : NSObject @property (nonatomic, strong) Child *child; @end
A retain cycle happens when two objects reference each other with strong, preventing deallocation. It is solved by making one of the references weak.
copy and assign
// copy: creates a copy (used with NSString) @property (nonatomic, copy) NSString *title; // assign: for primitive types (no ref count) @property (nonatomic) NSInteger age; @property (nonatomic) BOOL active; // unsafe_unretained: like weak but does not become nil @property (nonatomic, unsafe_unretained) id target;
copy creates a copy of the value (recommended for NSString and blocks). assign is for primitive types. unsafe_unretained is like weak but does not become nil — avoid it if possible.
weak/strong Dance in Blocks
// Avoid a retain cycle in blocks:
__weak typeof(self) weakSelf = self;
[self completeWithBlock:^{
__strong typeof(weakSelf) strongSelf = weakSelf;
if (!strongSelf) return;
[strongSelf doSomething];
}];In blocks that capture self, the weak/strong dance pattern is used: create a weakSelf to avoid the cycle and, inside the block, a temporary strongSelf to guarantee it exists during execution.
Properties
@interface Person : NSObject @property (nonatomic, strong) NSString *name; @property (nonatomic) NSInteger age; @end @implementation Person // Automatically generates: // - getter: -name // - setter: -setName: // - ivar: _name @end // nonatomic: faster (not thread-safe)
A @property automatically generates the getter, the setter and the instance variable (_name). The nonatomic attribute makes access faster, giving up thread-safety.
dealloc and Cleanup
@implementation MyClass
- (void)dealloc {
// Remove observers:
[[NSNotificationCenter defaultCenter]
removeObserver:self];
// Close manual resources:
[_file close];
// ARC handles the rest
}
@endThe dealloc method is called when the object is released. It is used to remove observers and close manual resources. With ARC, you do not call [super dealloc] nor free memory manually.
Protocols and Blocks
Protocols
// Define a protocol:
@protocol Healthy <NSObject>
- (void)exercise:(NSInteger)minutes;
@end
// Adopt it in the class:
@interface Person : NSObject <Healthy>
@end
@implementation Person
- (void)exercise:(NSInteger)minutes {
NSLog(@"Exercised %ld min", (long)minutes);
}
@endA @protocol defines a contract of methods (like an interface). The class adopts it between < > in the @interface and implements the required methods.
Blocks the Parameters
// Method that receives a block:
- (void)download:(NSURL *)url
completion:(void (^)(BOOL success))handler {
// ... asynchronous work ...
handler(YES);
}
// Call:
[self download:url completion:^(BOOL ok) {
if (ok) NSLog(@"Success!");
}];Blocks are often passed the method parameters (callbacks). The syntax void (^)(BOOL) means a block that receives a BOOL and returns nothing.
@required and @optional
@protocol Healthy <NSObject>
@required
- (void)exercise:(NSInteger)minutes;
@optional
- (void)meditate;
@end
// Check before calling an optional method:
if ([person respondsToSelector:@selector(meditate)]) {
[person meditate];
}@required methods are mandatory (the default); @optional ones are not. Before calling an optional method, check with respondsToSelector: to avoid crashes.
Block typedef
// typedef for readability:
typedef void (^CompletionHandler)(BOOL success, NSError *error);
// Use in a property:
@property (nonatomic, copy) CompletionHandler onComplete;
// Use in a method:
- (void)download:(NSURL *)url
completion:(CompletionHandler)handler {
handler(YES, nil);
}A typedef gives a readable name to a complex block type, making it easy to reuse in properties and method signatures. Block properties must be copy.
Delegates
@interface MyView : NSObject
@property (nonatomic, weak) id<MyDelegate> delegate;
@end
@implementation MyView
- (void)selectRow:(NSInteger)row {
[self.delegate didSelectRow:row];
}
@end
// delegate is weak to avoid a retain cycleThe delegate pattern lets one object report events to another. The delegate property is weak (to avoid cycles) and typed with the protocol that defines the callbacks.
respondsToSelector
// Check if an object responds to a method:
if ([object respondsToSelector:@selector(meditate)]) {
[object meditate];
}
// @selector creates a selector (method name)
SEL s = @selector(length);
[string performSelector:s];
// Useful for optional protocol methodsrespondsToSelector: checks at runtime whether an object implements a method, avoiding crashes with optional methods. @selector() represents a method name the a value.
Blocks
// Declare and use a block:
void (^myBlock)(void) = ^{
NSLog(@"Hello from the block!");
};
myBlock();
// With parameters and return value:
NSInteger (^add)(NSInteger, NSInteger) =
^NSInteger(NSInteger a, NSInteger b) {
return a + b;
};
NSLog(@"%ld", (long)add(3, 4)); // 7A block is a closure: code that captures its context. It is declared with ^. It can have parameters and a return value, and is widely used for callbacks and asynchronous code.
conformsToProtocol
// Check if it adopts a protocol:
if ([object conformsToProtocol:@protocol(Healthy)]) {
id<Healthy> h = (id<Healthy>)object;
[h exercise:30];
}
// isKindOfClass: checks the class
if ([object isKindOfClass:[Person class]]) {
Person *p = (Person *)object;
}conformsToProtocol: checks whether an object adopts a protocol, allowing a safe cast. isKindOfClass: does the same for the class. Both are runtime type checks.
Advanced
Categories
// NSString+Utils.h
@interface NSString (Utils)
- (BOOL)containsDigit;
@end
// NSString+Utils.m
@implementation NSString (Utils)
- (BOOL)containsDigit {
NSCharacterSet *ds =
[NSCharacterSet decimalDigitCharacterSet];
return [self rangeOfCharacterFromSet:ds]
.location != NSNotFound;
}
@endA category adds methods to an existing class without subclassing it, even system classes like NSString. The name goes between parentheses: NSString (Utils).
GCD (Grand Central Dispatch)
// Background + Main thread:
dispatch_async(dispatch_get_global_queue(
DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// Heavy work here
NSArray *data = [self process];
dispatch_async(dispatch_get_main_queue(), ^{
// Update the UI here
self.label.text = @"Done";
});
});GCD manages concurrency with queues. dispatch_async to a global queue runs work in the background; then it goes back to the main queue to update the UI (mandatory on iOS).
Class Extensions
// Person.m (at the top, before @implementation)
@interface Person ()
// Private properties:
@property (nonatomic) NSInteger counter;
- (void)internalMethod;
@end
@implementation Person
- (void)internalMethod {
self.counter++;
}
@endA class extension (anonymous category, ()) declares private properties and methods in the .m file. It is only visible inside the implementation, unlike named categories.
dispatch_after
// Run after a delay:
dispatch_after(
dispatch_time(DISPATCH_TIME_NOW,
2 * NSEC_PER_SEC),
dispatch_get_main_queue(), ^{
NSLog(@"After 2 seconds");
});
// dispatch_once: runs only once (singleton)
static dispatch_once_t once;
dispatch_once(&once, ^{
instance = [[self alloc] init];
});dispatch_after schedules a block with a delay (in nanoseconds). dispatch_once guarantees a block runs only once, being the classic pattern to implement singletons.
KVC (Key-Value Coding)
// Access properties by name (string): NSString *name = [person valueForKey:@"name"]; [person setValue:@"Anna" forKey:@"name"]; // Map over collections: NSArray *names = [people valueForKey:@"name"]; // Aggregation: NSNumber *max = [numbers valueForKeyPath:@"@max.self"];
KVC allows accessing properties by name (string) with valueForKey: and setValue:forKey:. The valueForKeyPath: supports aggregation operators like @max.
NSError
// NSError pattern (passed by reference):
NSError *error = nil;
NSString *content = [NSString
stringWithContentsOfFile:@"/tmp/f.txt"
encoding:NSUTF8StringEncoding
error:&error];
if (error) {
NSLog(@"Error: %@", error.localizedDescription);
NSLog(@"Code: %ld", (long)error.code);
}In Objective-C, errors use the NSError pattern passed by reference (&error). If as method fails, it fills in the error; check whether it is nil before using it.
KVO (Key-Value Observing)
// Observe a change on a property:
[person addObserver:self
forKeyPath:@"name"
options:NSKeyValueObservingOptionNew
context:NULL];
// Callback called on change:
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary *)change
context:(void *)context {
NSLog(@"New: %@", change[NSKeyValueChangeNewKey]);
}KVO notifies automatically when a property changes. You register with addObserver:forKeyPath: and receive the notification in observeValueForKeyPath: with the new value.
NSNotificationCenter
// Subscribe to a notification:
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:@selector(dataReceived:)
name:@"DataUpdated"
object:nil];
// Post a notification:
[[NSNotificationCenter defaultCenter]
postNotificationName:@"DataUpdated"
object:nil];
// Remove (in dealloc)NSNotificationCenter is a global publish/subscribe system. Subscribe with addObserver:selector:name: and post with postNotificationName:, decoupling objects.
Cocoa e Frameworks
Foundation vs UIKit
// Foundation: base types (macOS + iOS) #import <Foundation/Foundation.h> // NSString, NSArray, NSDictionary, NSURL... // UIKit: interface (iOS only) #import <UIKit/UIKit.h> // UIView, UIViewController, UILabel... // AppKit: interface (macOS only) #import <AppKit/AppKit.h>
The Foundation framework provides the base types (strings, collections, dates) on all platforms. UIKit (iOS) and AppKit (macOS) provide the graphical interface.
NSURL and NSURLSession
NSURL *url = [NSURL URLWithString:
@"https://api.example.com/data"];
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *task = [session
dataTaskWithURL:url
completionHandler:^(NSData *data,
NSURLResponse *resp, NSError *error) {
if (!error) {
// process the data
}
}];
[task resume];NSURL represents an address. NSURLSession makes asynchronous network requests; dataTaskWithURL: creates the task and the completionHandler receives the data. Call resume to start.
NSUserDefaults
NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults]; // Save: [prefs setObject:@"Anna" forKey:@"name"]; [prefs setInteger:30 forKey:@"age"]; [prefs setBool:YES forKey:@"active"]; [prefs synchronize]; // Read: NSString *name = [prefs stringForKey:@"name"]; NSInteger age = [prefs integerForKey:@"age"];
NSUserDefaults stores small persistent preferences (settings). Use setObject:forKey: to save and objectForKey: (or typed variants) to read.
NSDate and NSDateFormatter
// Current date: NSDate *now = [NSDate date]; // Format to string: NSDateFormatter *fmt = [[NSDateFormatter alloc] init]; [fmt setDateFormat:@"yyyy-MM-dd HH:mm"]; NSString *text = [fmt stringFromDate:now]; // Parse a string into a date: NSDate *date = [fmt dateFromString:@"2024-01-15 10:30"];
NSDate represents an instant in time. NSDateFormatter converts between dates and strings with a format defined by setDateFormat: (e.g. yyyy-MM-dd).
NSFileManager
NSFileManager *fm = [NSFileManager defaultManager];
// Check existence:
BOOL exists = [fm fileExistsAtPath:@"/tmp/f.txt"];
// Create a folder:
[fm createDirectoryAtPath:@"/tmp/folder"
withIntermediateDirectories:YES
attributes:nil error:nil];
// List contents:
NSArray *items = [fm contentsOfDirectoryAtPath:@"/tmp"
error:nil];NSFileManager manages the file system: check existence with fileExistsAtPath:, create folders with createDirectoryAtPath: and list contents with contentsOfDirectoryAtPath:.
JSON (NSJSONSerialization)
// JSON -> objects:
NSDictionary *json = [NSJSONSerialization
JSONObjectWithData:data
options:0 error:&error];
// objects -> JSON:
NSData *output = [NSJSONSerialization
dataWithJSONObject:dict
options:NSJSONWritingPrettyPrinted
error:&error];
NSString *text = [[NSString alloc]
initWithData:output encoding:NSUTF8StringEncoding];NSJSONSerialization converts between JSON and Foundation objects. JSONObjectWithData: parses NSData into dictionaries/arrays; dataWithJSONObject: does the reverse.
Tools and Patterns
.h / .m Structure
// Person.h (public interface)
#import <Foundation/Foundation.h>
@interface Person : NSObject
@property (nonatomic, copy) NSString *name;
- (NSString *)greeting;
@end
// Person.m (implementation)
#import "Person.h"
@implementation Person
- (NSString *)greeting {
return [NSString stringWithFormat:@"Hello, %@", self.name];
}
@endEach class splits into two files: the .h (header) declares the public interface and the .m (implementation) contains the code. The .m imports its own .h.
Modern Objective-C
// Literals (modern):
NSArray *arr = @[@1, @2, @3];
NSNumber *n = @42;
// Subscripting:
id obj = arr[0];
dict[@"key"] = value;
// instancetype instead of id:
+ (instancetype)create;
// NS_ENUM / NS_OPTIONS:
typedef NS_ENUM(NSInteger, Color) { ColorBlue };Modern Objective-C uses literals (@[], @42), subscripting (arr[0]), instancetype and NS_ENUM. These features make the code more concise and safer.
#import and #include
// #import: includes only once (recommended) #import "Person.h" // local header #import <Foundation/Foundation.h> // framework // #include: may include multiple times (C) #include "utils.h" // @class: forward declaration (in the .h) @class Person; // avoids importing the full header
#import includes a header guaranteeing it is only read once (preferable to #include). @class is a forward declaration used in the .h to avoid circular dependencies.
Best Practices
// 1. Properties instead of public ivars
@property (nonatomic, copy) NSString *name;
// 2. NSString the copy
// 3. delegate the weak
// 4. nil checks on optionals
if ([obj respondsToSelector:@selector(m)]) { }
// 5. Errors via NSError**, not exceptions
// 6. Immutability by default
// (NSArray, not NSMutableArray)Best practices: use properties, mark NSString the copy and delegate the weak, handle errors via NSError (not exceptions) and prefer immutable collections by default.
Compilation
# Compile with clang: clang -framework Foundation main.m -o program # Run: ./program # In Xcode: # Cmd+B -> build # Cmd+R -> build & run # Compiled files: # .m -> .o (object) -> executable
The clang compiler turns .m into an executable; the -framework Foundation flag links the framework. In Xcode, Cmd+B builds and Cmd+R runs.
Prefixes and Conventions
// 2-3 letter prefixes (avoid conflicts): @interface ABCPerson : NSObject @end // Naming conventions: // Classes: PascalCase (ABCPerson) // Methods: camelCase (countItems) // Properties: camelCase (firstName) // Constants: kPrefix (kMaxAttempts) static const NSInteger kMaxAttempts = 3;
Classes use a 2-3 letter prefix (e.g. NS, UI, ABC) to avoid name conflicts. Classes in PascalCase, methods and properties in camelCase, constants with k.