Cheatsheet Objective-C
Linguagem orientada a objetos da Apple para macOS e iOS
Objective-C
Básico
Tipos primitivos
int edad = 30; float pi = 3.14f; double preciso = 3.14159265; BOOL activo = YES; // o NO char c = 'A'; long grande = 1000000L; // Tipos platform-safe de Foundation: NSInteger count = 42; CGFloat x = 3.14;
Objective-C usa los tipos de C (int, float, double, char) más el BOOL (YES/NO). Prefiere NSInteger y CGFloat por ser seguros entre plataformas.
Literals modernos
// NSString:
NSString *nombre = @"Ana";
// NSNumber:
NSNumber *n = @42;
NSNumber *f = @3.14;
NSNumber *b = @YES;
// NSArray:
NSArray *arr = @[@"a", @"b", @"c"];
// NSDictionary:
NSDictionary *dict = @{
@"nombre": @"Ana",
@"edad": @30
};Los literals modernos usan @ para crear objetos de forma concisa: @42 (NSNumber), @[] (NSArray) y @{} (NSDictionary), sustituyendo los anchos métodos alloc/init.
Comentarios y estructura
// Comentario de línea /* Comentario de varias líneas */ /// Comentario de documentación (HeaderDoc) // Estructura típica de archivos: // Persona.h -> @interface (declaración) // Persona.m -> @implementation (código) #import "Persona.h" // importa header
Los comentarios usan // y /* */; el /// genera documentación. Cada clase se divide en .h (interfaz pública) y .m (implementación), unidos por #import.
Sintaxis de mensajes
// [receptor mensaje]: [array count]; [string length]; // Con argumentos (labels): [array objectAtIndex:0]; [dict objectForKey:@"clave"]; [string substringToIndex:5]; // Mensajes anidados: [[NSString alloc] initWithFormat:@"%d", 42];
Los métodos se llaman mediante mensajes entre corchetes: [receptor mensaje]. Cada argumento tiene un label descriptivo, haciendo las llamadas legibles.
NSLog y formato
NSLog(@"Texto simple"); NSLog(@"Edad: %d", 30); // int NSLog(@"Nombre: %@", nombre); // objeto NSLog(@"Pi: %.2f", 3.14159); // float NSLog(@"Count: %lu", (unsigned long)n); NSLog(@"Activo: %d", activo); // BOOL // %@ llama a -description del objeto
El NSLog imprime en la consola con especificadores de formato: %d (int), %f (float), %@ (objeto, llama a description). Es la herramienta básica de debug.
Control de flujo
if (edad >= 18) {
NSLog(@"Adulto");
} else {
NSLog(@"Menor");
}
for (int i = 0; i < 10; i++) { }
while (x > 0) { x--; }
// Fast enumeration:
for (NSString *item in array) {
NSLog(@"%@", item);
}Las estructuras de control vienen de C: if/else, for, while. El for...in (fast enumeration) recorre colecciones de forma simple y rápida.
Operadores
// Aritméticos: int s = 2 + 3; // 5 int d = 10 / 3; // 3 (entero) int r = 10 % 3; // 1 (resto) // Comparación: BOOL eq = (5 == 5); // YES BOOL gt = (5 > 3); // YES // Lógicos: BOOL y = (a && b); // AND BOOL o = (a || b); // OR BOOL no = !a; // NOT // Incremento: i++; ++i; i += 5;
Los operadores son los de C: aritméticos (+ - * / %), de comparación (== != > <) y lógicos (&& || !). La asignación compuesta (+=) y el incremento (++) también están disponibles.
switch
switch (dia) {
case 1:
NSLog(@"Lunes");
break;
case 2:
NSLog(@"Martes");
break;
default:
NSLog(@"Otro");
break;
}
// Ternario:
NSString *r = (x > 0) ? @"pos" : @"neg";El switch compara valores enteros, con case y default. Cada caso necesita break para no continuar al siguiente. El operador ternario ? : es un if inline.
nil messaging
Persona *p = nil;
// Enviar un mensaje a nil no crashea:
NSString *nombre = [p nombre]; // nil
NSInteger n = [p count]; // 0
BOOL flag = [p isValid]; // NO
// Útil para verificaciones:
if ([nombre length] > 0) {
// solo se ejecuta si nombre no es nil/vacío
}Enviar un mensaje a nil es seguro y no provoca crash — devuelve nil, 0 o NO. Esto elimina muchas verificaciones de null comunes en otros lenguajes.
Objetos e Classes
Declarar clase (@interface)
// Persona.h #import <Foundation/Foundation.h> @interface Persona : NSObject @property (nonatomic, strong) NSString *nombre; @property (nonatomic) NSInteger edad; - (NSString *)saludo; - (instancetype)initWithNombre:(NSString *)nombre; @end
La @interface en el archivo .h declara la clase, sus properties y los métodos públicos. Los métodos de instancia empiezan con -; la clase hereda de NSObject.
super y override
@implementation Estudiante
// Override de método del padre:
- (NSString *)saludo {
NSString *base = [super saludo];
return [base stringByAppendingString:
@" (estudiante)"];
}
@end
// super llama a la versión de la clase padrePara sobrescribir un método, se redefine en la subclase. El super llama a la implementación de la clase padre, permitiendo extender el comportamiento en vez de sustituirlo por completo.
Enumerados (NS_ENUM)
// Definir enum tipado:
typedef NS_ENUM(NSInteger, EstadoPedido) {
EstadoPendiente,
EstadoEnviado,
EstadoEntregado
};
// Usar:
EstadoPedido estado = EstadoEnviado;
if (estado == EstadoEnviado) {
NSLog(@"Enviado");
}
// NS_OPTIONS para flags (bitmask)La macro NS_ENUM define enumerados con tipo base explícito (ej.: NSInteger), más seguros que los enum de C. El NS_OPTIONS se usa para flags combinables (bitmask).
Implementación (@implementation)
// Persona.m
#import "Persona.h"
@implementation Persona
- (NSString *)saludo {
return [NSString stringWithFormat:
@"Hola, %@", self.nombre];
}
- (instancetype)initWithNombre:(NSString *)nombre {
self = [super init];
if (self) {
_nombre = nombre;
}
return self;
}
@endLa @implementation en el archivo .m contiene el código de los métodos. El self se refiere a la instancia actual y el _nombre es la variable de instancia (ivar) generada por la property.
Métodos de clase (+)
@interface Persona : NSObject
// Método de instancia (-):
- (NSString *)descripcion;
// Método de clase (+):
+ (instancetype)personaConNombre:(NSString *)nombre;
@end
@implementation Persona
+ (instancetype)personaConNombre:(NSString *)nombre {
Persona *p = [[Persona alloc] init];
p.nombre = nombre;
return p;
}
@end
// Uso: Persona *p = [Persona personaConNombre:@"Ana"];Los métodos de clase empiezan con + (los de instancia con -). Se usan frecuentemente como factory methods (constructores alternativos) que devuelven instancetype.
Crear objetos
// alloc + init: Persona *p = [[Persona alloc] init]; p.nombre = @"Ana"; p.edad = 30; // Con initializer custom: Persona *p2 = [[Persona alloc] initWithNombre:@"Ray"]; // Usar: NSLog(@"%@", [p saludo]); NSLog(@"%ld años", (long)p.edad);
Un objeto se crea con alloc (asigna memoria) seguido de init (inicializa). Las properties se acceden por punto (p.nombre) y los métodos por mensaje ([p saludo]).
id e instancetype
// id: puntero a cualquier objeto id objeto = @"una string"; objeto = @42; // ahora es NSNumber // Sin verificación de tipo en compile-time [objeto length]; // se ejecuta en runtime // instancetype: tipo de la clase receptora + (instancetype)crear; // devuelve el tipo correcto // Mejor que id en initializers/factories
El tipo id es un puntero a cualquier objeto, sin verificación de tipo en compilación. El instancetype es preferible en initializers y factories, pues indica el tipo real de la clase.
Herencia
// Estudiante.h
@interface Estudiante : Persona
@property (nonatomic, strong) NSString *escuela;
- (CGFloat)media;
@end
// Estudiante.m
@implementation Estudiante
- (CGFloat)media {
return 16.5;
}
@endLa herencia usa : ClasePadre en la @interface. La subclase hereda properties y métodos del padre y puede añadir los suyos. En Objective-C solo hay herencia simple.
description e isEqual
@implementation Persona
// Personalizar impresión (NSLog %@):
- (NSString *)description {
return [NSString stringWithFormat:
@"<Persona: %@, %ld>", self.nombre, (long)self.edad];
}
// Igualdad:
- (BOOL)isEqual:(id)other {
return [self.nombre isEqual:[other nombre]];
}
- (NSUInteger)hash {
return self.nombre.hash;
}
@endEl método description define el texto mostrado por NSLog(@"%@", obj). Para comparar objetos por valor, se sobrescriben isEqual: y hash en conjunto.
Strings e Coleções
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:@" "];
El NSString es inmutable. Métodos como uppercaseString, substringToIndex: y containsString: devuelven siempre una nueva string, sin alterar la original.
NSMutableArray
NSMutableArray *m = [NSMutableArray
arrayWithArray:@[@"Ana", @"Ray"]];
[m addObject:@"Zé"];
[m removeObjectAtIndex:1];
[m insertObject:@"X" atIndex:0];
[m replaceObjectAtIndex:0 withObject:@"Y"];
[m sortUsingSelector:@selector(compare:)];El NSMutableArray permite modificar la colección: addObject: añade, removeObjectAtIndex: elimina, insertObject:atIndex: inserta y sortUsingSelector: ordena.
NSMutableString
NSMutableString *ms = [NSMutableString
stringWithString:@"Hola Mundo"];
[ms appendString:@"!"];
[ms replaceOccurrencesOfString:@"Mundo"
withString:@"ObjC"
options:0
range:NSMakeRange(0, ms.length)];
[ms deleteCharactersInRange:NSMakeRange(0, 5)];El NSMutableString es la versión mutable: appendString: añade, replaceOccurrencesOfString: sustituye y deleteCharactersInRange: elimina, alterando la propia string.
NSDictionary
NSDictionary *dict = @{
@"nombre": @"Ana",
@"edad": @30
};
[dict objectForKey:@"nombre"]; // "Ana"
dict[@"nombre"]; // "Ana" (subscript)
[dict count]; // 2
[dict allKeys]; // todas las claves
[dict allValues]; // todos los valoresEl NSDictionary guarda pares clave-valor inmutables. El acceso usa objectForKey: o subscript dict[@"clave"]. Las claves deben ser únicas y no pueden ser nil.
Formato de strings
// stringWithFormat:
NSString *s = [NSString stringWithFormat:
@"%@ tiene %ld años", nombre, (long)edad];
// Con números:
NSString *precio = [NSString stringWithFormat:
@"%.2f EUR", 19.99];
// Juntar array:
NSString *lista = [array
componentsJoinedByString:@", "];El stringWithFormat: crea strings formateadas con especificadores (%@, %ld, %.2f). El componentsJoinedByString: junta los elementos de un array con un separador.
NSMutableDictionary
NSMutableDictionary *m = [NSMutableDictionary
dictionaryWithDictionary:@{@"nombre": @"Ana"}];
// Añadir / actualizar:
m[@"email"] = @"ana@mail.com";
[m setObject:@30 forKey:@"edad"];
// Eliminar:
[m removeObjectForKey:@"edad"];
[m removeAllObjects];El NSMutableDictionary permite modificar el diccionario: se asigna por subscript (m[@"clave"] = valor) o con setObject:forKey:, y se elimina con removeObjectForKey:.
NSArray
NSArray *arr = @[@"Ana", @"Ray", @"Mia"];
[arr count]; // 3
[arr objectAtIndex:0]; // "Ana"
arr[0]; // "Ana" (subscript)
[arr containsObject:@"Ray"]; // YES
[arr firstObject]; // "Ana"
[arr lastObject]; // "Mia"
// Recorrer:
for (NSString *nombre in arr) { }El NSArray es una colección ordenada e inmutable de objetos. El acceso usa objectAtIndex: o la sintaxis de subscript arr[0]. No acepta valores nil.
Enumeración de colecciones
// 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);
}];El for...in recorre arrays y diccionarios de forma simple. La enumeración por bloque (enumerateObjectsUsingBlock:) da acceso al índice y permite parar con *stop = YES.
Memória e Properties
ARC
// ARC (Automatic Reference Counting): // El compilador inserta retain/release // No se llama a [obj release] manualmente Persona *p = [[Persona alloc] init]; // p tiene strong reference p = nil; // libera el objeto (sin refs) // ARC gestiona la memoria en compile-time
El ARC (Automatic Reference Counting) gestiona la memoria automáticamente en tiempo de compilación, insertando retain/release. Nunca se llama a release manualmente.
readonly y getter custom
@interface Persona : NSObject
// Solo lectura (sin setter público):
@property (nonatomic, readonly) NSInteger edad;
// Getter con nombre custom:
@property (nonatomic, getter=isActivo) BOOL activo;
@end
// Uso:
if (persona.isActivo) { } // getter custom
persona.edad = 30; // ERROR (readonly)El atributo readonly impide la generación del setter público. El getter=isActivo personaliza el nombre del getter, convención común para propiedades booleanas.
strong y weak
// strong (default): mantiene el objeto vivo @property (nonatomic, strong) NSString *nombre; // weak: no retiene (queda nil al liberarse) @property (nonatomic, weak) id<Delegate> delegate; // Ejemplo: Persona * __weak debil = persona; persona = nil; NSLog(@"%@", debil); // (null)
Una referencia strong mantiene el objeto vivo; una weak no cuenta para el reference count y queda automáticamente nil cuando el objeto se libera, evitando ciclos.
Retain cycles
// Problema: A strong -> B y B strong -> A // Ninguno se libera (memory leak) // Solución: una referencia weak @interface Hijo : NSObject @property (nonatomic, weak) Padre *padre; // weak! @end @interface Padre : NSObject @property (nonatomic, strong) Hijo *hijo; @end
Un retain cycle ocurre cuando dos objetos se referencian mutuamente con strong, impidiendo la liberación. Se resuelve haciendo una de las referencias weak.
copy y assign
// copy: crea una copia (usado en NSString) @property (nonatomic, copy) NSString *título; // assign: para tipos primitivos (sin ref count) @property (nonatomic) NSInteger edad; @property (nonatomic) BOOL activo; // unsafe_unretained: como weak pero no queda nil @property (nonatomic, unsafe_unretained) id objetivo;
El copy crea una copia del valor (recomendado para NSString y bloques). El assign es para tipos primitivos. El unsafe_unretained es como weak pero no queda nil — evítalo si es posible.
weak/strong dance en bloques
// Evitar retain cycle en bloques:
__weak typeof(self) weakSelf = self;
[self completarConBloque:^{
__strong typeof(weakSelf) strongSelf = weakSelf;
if (!strongSelf) return;
[strongSelf hacerAlgo];
}];En bloques que capturan self, se usa el patrón weak/strong dance: se crea un weakSelf para evitar el ciclo y, dentro del bloque, un strongSelf temporal para garantizar que existe durante la ejecución.
Properties
@interface Persona : NSObject @property (nonatomic, strong) NSString *nombre; @property (nonatomic) NSInteger edad; @end @implementation Persona // Genera automáticamente: // - getter: -nombre // - setter: -setNombre: // - ivar: _nombre @end // nonatomic: más rápido (no thread-safe)
Una @property genera automáticamente el getter, el setter y la variable de instancia (_nombre). El atributo nonatomic hace el acceso más rápido, renunciando a thread-safety.
dealloc y cleanup
@implementation MiClase
- (void)dealloc {
// Eliminar observers:
[[NSNotificationCenter defaultCenter]
removeObserver:self];
// Cerrar recursos manuales:
[_archivo close];
// ARC se encarga del resto
}
@endEl método dealloc se llama cuando el objeto se libera. Sirve para eliminar observers y cerrar recursos manuales. Con ARC, no se llama a [super dealloc] ni se libera memoria manualmente.
Protocolos e Blocos
Protocolos
// Definir protocolo:
@protocol Saludable <NSObject>
- (void)ejercitar:(NSInteger)minutos;
@end
// Adoptar en la clase:
@interface Persona : NSObject <Saludable>
@end
@implementation Persona
- (void)ejercitar:(NSInteger)minutos {
NSLog(@"Ejercitó %ld min", (long)minutos);
}
@endUn @protocol define un contrato de métodos (como una interfaz). La clase lo adopta entre < > en la @interface e implementa los métodos exigidos.
Bloques como parámetro
// Método que recibe bloque:
- (void)descargar:(NSURL *)url
completion:(void (^)(BOOL exito))handler {
// ... trabajo asíncrono ...
handler(YES);
}
// Llamar:
[self descargar:url completion:^(BOOL ok) {
if (ok) NSLog(@"¡Éxito!");
}];Los bloques se pasan frecuentemente como parámetros de métodos (callbacks). La sintaxis void (^)(BOOL) indica un bloque que recibe BOOL y no devuelve nada.
@required y @optional
@protocol Saludable <NSObject>
@required
- (void)ejercitar:(NSInteger)minutos;
@optional
- (void)meditar;
@end
// Verificar antes de llamar al opcional:
if ([persona respondsToSelector:@selector(meditar)]) {
[persona meditar];
}Los métodos @required son obligatorios (por defecto); los @optional no. Antes de llamar a un método opcional, se verifica con respondsToSelector: para evitar crashes.
typedef de bloques
// typedef para legibilidad:
typedef void (^CompletionHandler)(BOOL exito, NSError *error);
// Usar en property:
@property (nonatomic, copy) CompletionHandler alCompletar;
// Usar en método:
- (void)descargar:(NSURL *)url
completion:(CompletionHandler)handler {
handler(YES, nil);
}Un typedef da un nombre legible a un tipo de bloque complejo, facilitando la reutilización en properties y firmas de métodos. La property de bloque debe ser copy.
Delegates
@interface MiVista : NSObject
@property (nonatomic, weak) id<MiDelegate> delegate;
@end
@implementation MiVista
- (void)seleccionarFila:(NSInteger)row {
[self.delegate didSelectRow:row];
}
@end
// delegate es weak para evitar retain cycleEl patrón delegate permite que un objeto comunique eventos a otro. La property delegate es weak (para evitar ciclos) y tipada con el protocolo que define los callbacks.
respondsToSelector
// Verificar si el objeto responde a un método:
if ([objeto respondsToSelector:@selector(meditar)]) {
[objeto meditar];
}
// @selector crea un selector (nombre de método)
SEL s = @selector(length);
[string performSelector:s];
// Útil para métodos opcionales de protocolosEl respondsToSelector: verifica en runtime si un objeto implementa un método, evitando crashes con métodos opcionales. El @selector() representa el nombre de un método como valor.
Bloques
// Declarar y usar bloque:
void (^miBloque)(void) = ^{
NSLog(@"¡Hola desde el bloque!");
};
miBloque();
// Con parámetros y retorno:
NSInteger (^suma)(NSInteger, NSInteger) =
^NSInteger(NSInteger a, NSInteger b) {
return a + b;
};
NSLog(@"%ld", (long)suma(3, 4)); // 7Un bloque es una closure: código que captura el contexto. Se declara con ^. Puede tener parámetros y retorno, y se usa mucho para callbacks y código asíncrono.
conformsToProtocol
// Verificar si adopta un protocolo:
if ([objeto conformsToProtocol:@protocol(Saludable)]) {
id<Saludable> s = (id<Saludable>)objeto;
[s ejercitar:30];
}
// isKindOfClass: verifica la clase
if ([objeto isKindOfClass:[Persona class]]) {
Persona *p = (Persona *)objeto;
}El conformsToProtocol: verifica si un objeto adopta un protocolo, permitiendo un cast seguro. El isKindOfClass: hace lo mismo para la clase. Ambas son verificaciones de tipo en runtime.
Avançado
Categorías
// NSString+Utils.h
@interface NSString (Utils)
- (BOOL)contieneDigito;
@end
// NSString+Utils.m
@implementation NSString (Utils)
- (BOOL)contieneDigito {
NSCharacterSet *ds =
[NSCharacterSet decimalDigitCharacterSet];
return [self rangeOfCharacterFromSet:ds]
.location != NSNotFound;
}
@endUna categoría añade métodos a una clase existente sin subclasificarla, incluso clases del sistema como NSString. El nombre va entre paréntesis: NSString (Utils).
GCD (Grand Central Dispatch)
// Background + Main thread:
dispatch_async(dispatch_get_global_queue(
DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// Trabajo pesado aquí
NSArray *datos = [self procesar];
dispatch_async(dispatch_get_main_queue(), ^{
// Actualizar la UI aquí
self.label.text = @"Hecho";
});
});El GCD gestiona la concurrencia con colas. El dispatch_async a una cola global ejecuta trabajo en background; después vuelve a la main queue para actualizar la UI (obligatorio en iOS).
Extensiones de clase
// Persona.m (arriba, antes de la @implementation)
@interface Persona ()
// Properties privadas:
@property (nonatomic) NSInteger contador;
- (void)metodoInterno;
@end
@implementation Persona
- (void)metodoInterno {
self.contador++;
}
@endUna class extension (categoría anónima, ()) declara properties y métodos privados en el archivo .m. Solo es visible dentro de la implementación, a diferencia de las categorías con nombre.
dispatch_after
// Ejecutar tras un retraso:
dispatch_after(
dispatch_time(DISPATCH_TIME_NOW,
2 * NSEC_PER_SEC),
dispatch_get_main_queue(), ^{
NSLog(@"Tras 2 segundos");
});
// dispatch_once: se ejecuta solo una vez (singleton)
static dispatch_once_t once;
dispatch_once(&once, ^{
instancia = [[self alloc] init];
});El dispatch_after programa un bloque con retraso (en nanosegundos). El dispatch_once garantiza que un bloque se ejecuta solo una vez, siendo el patrón clásico para implementar singletons.
KVC (Key-Value Coding)
// Acceder a properties por nombre (string): NSString *nombre = [persona valueForKey:@"nombre"]; [persona setValue:@"Ana" forKey:@"nombre"]; // Recorrer colecciones: NSArray *nombres = [personas valueForKey:@"nombre"]; // Agregación: NSNumber *max = [numeros valueForKeyPath:@"@max.self"];
El KVC permite acceder a properties por nombre (string) con valueForKey: y setValue:forKey:. El valueForKeyPath: soporta operadores de agregación como @max.
NSError
// Patrón NSError (pasado por referencia):
NSError *error = nil;
NSString *contenido = [NSString
stringWithContentsOfFile:@"/tmp/f.txt"
encoding:NSUTF8StringEncoding
error:&error];
if (error) {
NSLog(@"Error: %@", error.localizedDescription);
NSLog(@"Código: %ld", (long)error.code);
}En Objective-C, los errores usan el patrón NSError pasado por referencia (&error). Si el método falla, rellena el error; se verifica si es nil antes de usarlo.
KVO (Key-Value Observing)
// Observar cambio en una property:
[persona addObserver:self
forKeyPath:@"nombre"
options:NSKeyValueObservingOptionNew
context:NULL];
// Callback llamado en el cambio:
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary *)change
context:(void *)context {
NSLog(@"Nuevo: %@", change[NSKeyValueChangeNewKey]);
}El KVO notifica automáticamente cuando una property cambia. Se registra con addObserver:forKeyPath: y se recibe la notificación en observeValueForKeyPath: con el valor nuevo.
NSNotificationCenter
// Suscribirse a una notificación:
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:@selector(datosRecibidos:)
name:@"DatosActualizados"
object:nil];
// Enviar notificación:
[[NSNotificationCenter defaultCenter]
postNotificationName:@"DatosActualizados"
object:nil];
// Eliminar (en dealloc)El NSNotificationCenter es un sistema de publicación/suscripción global. Se suscribe con addObserver:selector:name: y se envía con postNotificationName:, desacoplando objetos.
Cocoa e Frameworks
Foundation vs UIKit
// Foundation: tipos base (macOS + iOS) #import <Foundation/Foundation.h> // NSString, NSArray, NSDictionary, NSURL... // UIKit: interfaz (solo iOS) #import <UIKit/UIKit.h> // UIView, UIViewController, UILabel... // AppKit: interfaz (solo macOS) #import <AppKit/AppKit.h>
El framework Foundation proporciona los tipos base (strings, colecciones, fechas) en todas las plataformas. El UIKit (iOS) y el AppKit (macOS) proporcionan la interfaz gráfica.
NSURL y NSURLSession
NSURL *url = [NSURL URLWithString:
@"https://api.ejemplo.com/datos"];
NSURLSession *sesion = [NSURLSession sharedSession];
NSURLSessionDataTask *task = [sesion
dataTaskWithURL:url
completionHandler:^(NSData *datos,
NSURLResponse *resp, NSError *error) {
if (!error) {
// procesar datos
}
}];
[task resume];El NSURL representa una dirección. El NSURLSession hace peticiones de red asíncronas; el dataTaskWithURL: crea la tarea y el completionHandler recibe los datos. Se llama a resume para iniciar.
NSUserDefaults
NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults]; // Guardar: [prefs setObject:@"Ana" forKey:@"nombre"]; [prefs setInteger:30 forKey:@"edad"]; [prefs setBool:YES forKey:@"activo"]; [prefs synchronize]; // Leer: NSString *nombre = [prefs stringForKey:@"nombre"]; NSInteger edad = [prefs integerForKey:@"edad"];
El NSUserDefaults guarda pequeñas preferencias persistentes (ajustes). Usa setObject:forKey: para guardar y objectForKey: (o variantes tipadas) para leer.
NSDate y NSDateFormatter
// Fecha actual: NSDate *ahora = [NSDate date]; // Formatear a string: NSDateFormatter *fmt = [[NSDateFormatter alloc] init]; [fmt setDateFormat:@"yyyy-MM-dd HH:mm"]; NSString *texto = [fmt stringFromDate:ahora]; // Parse de string a fecha: NSDate *fecha = [fmt dateFromString:@"2024-01-15 10:30"];
El NSDate representa un instante. El NSDateFormatter convierte entre fechas y strings con un formato definido por setDateFormat: (ej.: yyyy-MM-dd).
NSFileManager
NSFileManager *fm = [NSFileManager defaultManager];
// Verificar existencia:
BOOL existe = [fm fileExistsAtPath:@"/tmp/f.txt"];
// Crear carpeta:
[fm createDirectoryAtPath:@"/tmp/carpeta"
withIntermediateDirectories:YES
attributes:nil error:nil];
// Listar contenido:
NSArray *items = [fm contentsOfDirectoryAtPath:@"/tmp"
error:nil];El NSFileManager gestiona el sistema de archivos: verifica existencia con fileExistsAtPath:, crea carpetas con createDirectoryAtPath: y lista contenido con contentsOfDirectoryAtPath:.
JSON (NSJSONSerialization)
// JSON -> objetos:
NSDictionary *json = [NSJSONSerialization
JSONObjectWithData:datos
options:0 error:&error];
// objetos -> JSON:
NSData *salida = [NSJSONSerialization
dataWithJSONObject:dict
options:NSJSONWritingPrettyPrinted
error:&error];
NSString *texto = [[NSString alloc]
initWithData:salida encoding:NSUTF8StringEncoding];El NSJSONSerialization convierte entre JSON y objetos Foundation. JSONObjectWithData: parsea NSData a diccionarios/arrays; dataWithJSONObject: hace lo inverso.
Ferramentas e Padrões
Estructura .h / .m
// Persona.h (interfaz pública)
#import <Foundation/Foundation.h>
@interface Persona : NSObject
@property (nonatomic, copy) NSString *nombre;
- (NSString *)saludo;
@end
// Persona.m (implementación)
#import "Persona.h"
@implementation Persona
- (NSString *)saludo {
return [NSString stringWithFormat:@"Hola, %@", self.nombre];
}
@endCada clase se divide en dos archivos: el .h (header) declara la interfaz pública y el .m (implementation) contiene el código. El .m importa su propio .h.
Objective-C moderno
// Literals (moderno):
NSArray *arr = @[@1, @2, @3];
NSNumber *n = @42;
// Subscripting:
id obj = arr[0];
dict[@"clave"] = valor;
// instancetype en vez de id:
+ (instancetype)crear;
// NS_ENUM / NS_OPTIONS:
typedef NS_ENUM(NSInteger, Color) { ColorAzul };El Objective-C moderno usa literals (@[], @42), subscripting (arr[0]), instancetype y NS_ENUM. Estas features hacen el código más conciso y seguro.
#import e #include
// #import: incluye una sola vez (recomendado) #import "Persona.h" // header local #import <Foundation/Foundation.h> // framework // #include: puede incluir varias veces (C) #include "utils.h" // @class: forward declaration (en el .h) @class Persona; // evita importar el header completo
El #import incluye un header garantizando que solo se lee una vez (preferible al #include). El @class es una forward declaration usada en el .h para evitar dependencias circulares.
Buenas prácticas
// 1. Properties en vez de ivars públicos
@property (nonatomic, copy) NSString *nombre;
// 2. NSString como copy
// 3. delegate como weak
// 4. nil checks en opcionales
if ([obj respondsToSelector:@selector(m)]) { }
// 5. Errores vía NSError**, no excepciones
// 6. Inmutabilidad por defecto
// (NSArray, no NSMutableArray)Buenas prácticas: usa properties, marca NSString como copy y delegate como weak, trata errores vía NSError (no excepciones) y prefiere colecciones inmutables por defecto.
Compilación
# Compilar con clang: clang -framework Foundation main.m -o programa # Ejecutar: ./programa # En Xcode: # Cmd+B -> build # Cmd+R -> build & run # Archivos compilados: # .m -> .o (object) -> ejecutable
El compilador clang transforma .m en ejecutable; la flag -framework Foundation enlaza el framework. En Xcode, Cmd+B compila y Cmd+R ejecuta.
Prefijos y convenciones
// Prefijos de 2-3 letras (evitan conflictos): @interface ABCPersona : NSObject @end // Convenciones de nombres: // Clases: PascalCase (ABCPersona) // Métodos: camelCase (contarItems) // Properties: camelCase (primerNombre) // Constantes: kPrefijo (kMaxIntentos) static const NSInteger kMaxIntentos = 3;
Las clases usan un prefijo de 2-3 letras (ej.: NS, UI, ABC) para evitar conflictos de nombres. Clases en PascalCase, métodos y properties en camelCase, constantes con k.