DevTools

Cheatsheet Objective-C

Linguagem orientada a objetos da Apple para macOS e iOS

Volver a los lenguajes
Objective-C
62 tarjetas encontradas
Categorías:
Versiones:

Básico


9 cards
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


9 cards
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 padre

Para 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;
}

@end

La @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;
}
@end

La 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;
}
@end

El 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


8 cards
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 valores

El 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


8 cards
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
}

@end

El 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


8 cards
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);
}
@end

Un @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 cycle

El 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 protocolos

El 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));   // 7

Un 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


8 cards
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;
}
@end

Una 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++;
}
@end

Una 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


6 cards
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


6 cards
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];
}
@end

Cada 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.