DevTools

Cheatsheet Objective-C

Linguagem orientada a objetos da Apple para macOS e iOS

Voltar às linguagens
Objective-C
62 cards encontrados
Categorias:
Versões:

Básico


9 cards
Tipos primitivos
int idade = 30;
float pi = 3.14f;
double preciso = 3.14159265;
BOOL ativo = YES;        // ou NO
char c = 'A';
long grande = 1000000L;

// Tipos platform-safe da Foundation:
NSInteger count = 42;
CGFloat x = 3.14;

O Objective-C usa os tipos do C (int, float, double, char) mais o BOOL (YES/NO). Prefere NSInteger e CGFloat por serem seguros entre plataformas.

Literals modernos
// NSString:
NSString *nome = @"Ana";

// NSNumber:
NSNumber *n = @42;
NSNumber *f = @3.14;
NSNumber *b = @YES;

// NSArray:
NSArray *arr = @[@"a", @"b", @"c"];

// NSDictionary:
NSDictionary *dict = @{
    @"nome": @"Ana",
    @"idade": @30
};

Os literals modernos usam @ para criar objetos de forma concisa: @42 (NSNumber), @[] (NSArray) e @{} (NSDictionary), substituindo os longos métodos alloc/init.

Comentários e estrutura
// Comentário de linha

/* Comentário
   de várias linhas */

/// Comentário de documentação (HeaderDoc)

// Estrutura típica de ficheiros:
// Pessoa.h  -> @interface (declaração)
// Pessoa.m  -> @implementation (código)

#import "Pessoa.h"   // importa header

Os comentários usam // e /* */; o /// gera documentação. Cada classe divide-se em .h (interface pública) e .m (implementação), ligados por #import.

Sintaxe de mensagens
// [receptor mensagem]:
[array count];
[string length];

// Com argumentos (labels):
[array objectAtIndex:0];
[dict objectForKey:@"chave"];
[string substringToIndex:5];

// Mensagens aninhadas:
[[NSString alloc] initWithFormat:@"%d", 42];

Os métodos são chamados por mensagens entre parênteses retos: [receptor mensagem]. Cada argumento tem um label descritivo, tornando as chamadas legíveis.

NSLog e formatação
NSLog(@"Texto simples");
NSLog(@"Idade: %d", 30);          // int
NSLog(@"Nome: %@", nome);         // objeto
NSLog(@"Pi: %.2f", 3.14159);      // float
NSLog(@"Count: %lu", (unsigned long)n);
NSLog(@"Ativo: %d", ativo);       // BOOL

// %@ chama -description do objeto

O NSLog imprime no console com especificadores de formato: %d (int), %f (float), %@ (objeto, chama description). É a ferramenta básica de debug.

Controlo de fluxo
if (idade >= 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);
}

As estruturas de controlo vêm do C: if/else, for, while. O for...in (fast enumeration) percorre coleções de forma simples e rápida.

Operadores
// Aritméticos:
int s = 2 + 3;     // 5
int d = 10 / 3;    // 3 (inteiro)
int r = 10 % 3;    // 1 (resto)

// Comparação:
BOOL eq = (5 == 5);   // YES
BOOL gt = (5 > 3);    // YES

// Lógicos:
BOOL e = (a && b);    // AND
BOOL ou = (a || b);   // OR
BOOL nao = !a;        // NOT

// Incremento:
i++;  ++i;  i += 5;

Os operadores são os do C: aritméticos (+ - * / %), de comparação (== != > <) e lógicos (&& || !). A atribuição composta (+=) e o incremento (++) também estão disponíveis.

switch
switch (dia) {
    case 1:
        NSLog(@"Segunda");
        break;
    case 2:
        NSLog(@"Terca");
        break;
    default:
        NSLog(@"Outro");
        break;
}

// Ternário:
NSString *r = (x > 0) ? @"pos" : @"neg";

O switch compara valores inteiros, com case e default. Cada caso precisa de break para não continuar para o seguinte. O operador ternário ? : é um if inline.

nil messaging
Pessoa *p = nil;

// Enviar mensagem a nil não crasha:
NSString *nome = [p nome];   // nil
NSInteger n = [p count];     // 0
BOOL flag = [p isValid];     // NO

// Útil para verificações:
if ([nome length] > 0) {
    // só corre se nome não for nil/vazio
}

Enviar uma mensagem a nil é seguro e não provoca crash — devolve nil, 0 ou NO. Isto elimina muitas verificações de null comuns noutras linguagens.

Objetos e Classes


9 cards
Declarar classe (@interface)
// Pessoa.h
#import <Foundation/Foundation.h>

@interface Pessoa : NSObject

@property (nonatomic, strong) NSString *nome;
@property (nonatomic) NSInteger idade;

- (NSString *)saudacao;
- (instancetype)initWithNome:(NSString *)nome;

@end

A @interface no ficheiro .h declara a classe, as suas properties e os métodos públicos. Os métodos de instância começam com -; a classe herda de NSObject.

super e override
@implementation Estudante

// Override de método do pai:
- (NSString *)saudacao {
    NSString *base = [super saudacao];
    return [base stringByAppendingString:
        @" (estudante)"];
}

@end

// super chama a versão da classe pai

Para sobrescrever um método, redefine-se na subclasse. O super chama a implementação da classe pai, permitindo estender o comportamento em vez de o substituir por completo.

Enumerados (NS_ENUM)
// Definir enum tipado:
typedef NS_ENUM(NSInteger, EstadoPedido) {
    EstadoPendente,
    EstadoEnviado,
    EstadoEntregue
};

// Usar:
EstadoPedido estado = EstadoEnviado;

if (estado == EstadoEnviado) {
    NSLog(@"Enviado");
}

// NS_OPTIONS para flags (bitmask)

O macro NS_ENUM define enumerados com tipo base explícito (ex.: NSInteger), mais seguros que os enum do C. O NS_OPTIONS é usado para flags combináveis (bitmask).

Implementação (@implementation)
// Pessoa.m
#import "Pessoa.h"

@implementation Pessoa

- (NSString *)saudacao {
    return [NSString stringWithFormat:
        @"Olá, %@", self.nome];
}

- (instancetype)initWithNome:(NSString *)nome {
    self = [super init];
    if (self) {
        _nome = nome;
    }
    return self;
}

@end

A @implementation no ficheiro .m contém o código dos métodos. O self refere-se à instância atual e o _nome é a variável de instância (ivar) gerada pela property.

Métodos de classe (+)
@interface Pessoa : NSObject
// Método de instância (-):
- (NSString *)descricao;
// Método de classe (+):
+ (instancetype)pessoaComNome:(NSString *)nome;
@end

@implementation Pessoa
+ (instancetype)pessoaComNome:(NSString *)nome {
    Pessoa *p = [[Pessoa alloc] init];
    p.nome = nome;
    return p;
}
@end

// Uso: Pessoa *p = [Pessoa pessoaComNome:@"Ana"];

Métodos de classe começam com + (os de instância com -). São frequentemente usados como factory methods (construtores alternativos) que devolvem instancetype.

Criar objetos
// alloc + init:
Pessoa *p = [[Pessoa alloc] init];
p.nome = @"Ana";
p.idade = 30;

// Com initializer custom:
Pessoa *p2 = [[Pessoa alloc] initWithNome:@"Rui"];

// Usar:
NSLog(@"%@", [p saudacao]);
NSLog(@"%ld anos", (long)p.idade);

Um objeto cria-se com alloc (aloca memória) seguido de init (inicializa). As properties acedem-se por ponto (p.nome) e os métodos por mensagem ([p saudacao]).

id e instancetype
// id: ponteiro para qualquer objeto
id objeto = @"uma string";
objeto = @42;   // agora é NSNumber

// Sem verificação de tipo em compile-time
[objeto length];   // corre em runtime

// instancetype: tipo da classe recetora
+ (instancetype)novo;   // retorna o tipo certo

// Melhor que id em initializers/factories

O tipo id é um ponteiro para qualquer objeto, sem verificação de tipo em compilação. O instancetype é preferível em initializers e factories, pois indica o tipo real da classe.

Herança
// Estudante.h
@interface Estudante : Pessoa
@property (nonatomic, strong) NSString *escola;
- (CGFloat)media;
@end

// Estudante.m
@implementation Estudante
- (CGFloat)media {
    return 16.5;
}
@end

A herança usa : ClassePai na @interface. A subclasse herda properties e métodos do pai e pode adicionar os seus. Em Objective-C só há herança simples.

description e isEqual
@implementation Pessoa

// Personalizar impressão (NSLog %@):
- (NSString *)description {
    return [NSString stringWithFormat:
        @"<Pessoa: %@, %ld>", self.nome, (long)self.idade];
}

// Igualdade:
- (BOOL)isEqual:(id)other {
    return [self.nome isEqual:[other nome]];
}

- (NSUInteger)hash {
    return self.nome.hash;
}
@end

O método description define o texto mostrado por NSLog(@"%@", obj). Para comparar objetos por valor, sobrescreve-se isEqual: e hash em 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:@" "];

O NSString é imutável. Métodos como uppercaseString, substringToIndex: e containsString: devolvem sempre uma nova string, sem alterar a original.

NSMutableArray
NSMutableArray *m = [NSMutableArray
    arrayWithArray:@[@"Ana", @"Rui"]];

[m addObject:@"Zé"];
[m removeObjectAtIndex:1];
[m insertObject:@"X" atIndex:0];
[m replaceObjectAtIndex:0 withObject:@"Y"];

[m sortUsingSelector:@selector(compare:)];

O NSMutableArray permite modificar a coleção: addObject: adiciona, removeObjectAtIndex: remove, insertObject:atIndex: insere e sortUsingSelector: ordena.

NSMutableString
NSMutableString *ms = [NSMutableString
    stringWithString:@"Olá Mundo"];

[ms appendString:@"!"];
[ms replaceOccurrencesOfString:@"Mundo"
    withString:@"ObjC"
    options:0
    range:NSMakeRange(0, ms.length)];

[ms deleteCharactersInRange:NSMakeRange(0, 4)];

O NSMutableString é a versão mutável: appendString: acrescenta, replaceOccurrencesOfString: substitui e deleteCharactersInRange: remove, alterando a própria string.

NSDictionary
NSDictionary *dict = @{
    @"nome": @"Ana",
    @"idade": @30
};

[dict objectForKey:@"nome"];   // "Ana"
dict[@"nome"];                 // "Ana" (subscript)
[dict count];                  // 2
[dict allKeys];                // todas as chaves
[dict allValues];              // todos os valores

O NSDictionary guarda pares chave-valor imutáveis. O acesso usa objectForKey: ou subscript dict[@"chave"]. As chaves devem ser únicas e não podem ser nil.

Formatação de strings
// stringWithFormat:
NSString *s = [NSString stringWithFormat:
    @"%@ tem %ld anos", nome, (long)idade];

// Com números:
NSString *preco = [NSString stringWithFormat:
    @"%.2f EUR", 19.99];

// Juntar array:
NSString *lista = [array
    componentsJoinedByString:@", "];

O stringWithFormat: cria strings formatadas com especificadores (%@, %ld, %.2f). O componentsJoinedByString: junta os elementos de um array com um separador.

NSMutableDictionary
NSMutableDictionary *m = [NSMutableDictionary
    dictionaryWithDictionary:@{@"nome": @"Ana"}];

// Adicionar / atualizar:
m[@"email"] = @"ana@mail.com";
[m setObject:@30 forKey:@"idade"];

// Remover:
[m removeObjectForKey:@"idade"];

[m removeAllObjects];

O NSMutableDictionary permite alterar o dicionário: atribui-se por subscript (m[@"chave"] = valor) ou com setObject:forKey:, e remove-se com removeObjectForKey:.

NSArray
NSArray *arr = @[@"Ana", @"Rui", @"Mia"];

[arr count];                 // 3
[arr objectAtIndex:0];       // "Ana"
arr[0];                      // "Ana" (subscript)
[arr containsObject:@"Rui"]; // YES
[arr firstObject];           // "Ana"
[arr lastObject];            // "Mia"

// Percorrer:
for (NSString *nome in arr) { }

O NSArray é uma coleção ordenada e imutável de objetos. O acesso usa objectAtIndex: ou a sintaxe de subscript arr[0]. Não aceita valores nil.

Enumeração de coleções
// 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);
}];

O for...in percorre arrays e dicionários de forma simples. A enumeração por bloco (enumerateObjectsUsingBlock:) dá acesso ao índice e permite parar com *stop = YES.

Memória e Properties


8 cards
ARC
// ARC (Automatic Reference Counting):
// O compilador insere retain/release

// Não se chama [obj release] manualmente
Pessoa *p = [[Pessoa alloc] init];
// p tem strong reference

p = nil;   // liberta o objeto (sem refs)

// ARC trata da memória em compile-time

O ARC (Automatic Reference Counting) gere a memória automaticamente em tempo de compilação, inserindo retain/release. Nunca se chama release manualmente.

readonly e getter custom
@interface Pessoa : NSObject
// Só leitura (sem setter público):
@property (nonatomic, readonly) NSInteger idade;

// Getter com nome custom:
@property (nonatomic, getter=isAtivo) BOOL ativo;
@end

// Uso:
if (pessoa.isAtivo) { }   // getter custom
pessoa.idade = 30;        // ERRO (readonly)

O atributo readonly impede a geração do setter público. O getter=isAtivo personaliza o nome do getter, convenção comum para propriedades booleanas.

strong e weak
// strong (default): mantém o objeto vivo
@property (nonatomic, strong) NSString *nome;

// weak: não mantém (fica nil quando libertado)
@property (nonatomic, weak) id<Delegate> delegate;

// Exemplo:
Pessoa * __weak fraco = pessoa;
pessoa = nil;
NSLog(@"%@", fraco);   // (null)

Uma referência strong mantém o objeto vivo; uma weak não conta para o reference count e fica automaticamente nil quando o objeto é libertado, evitando ciclos.

Retain cycles
// Problema: A strong -> B e B strong -> A
// Nenhum é libertado (memory leak)

// Solução: uma referência weak
@interface Filho : NSObject
@property (nonatomic, weak) Pai *pai;   // weak!
@end

@interface Pai : NSObject
@property (nonatomic, strong) Filho *filho;
@end

Um retain cycle ocorre quando dois objetos se referenciam mutuamente com strong, impedindo a libertação. Resolve-se tornando uma das referências weak.

copy e assign
// copy: cria uma cópia (usado em NSString)
@property (nonatomic, copy) NSString *titulo;

// assign: para tipos primitivos (sem ref count)
@property (nonatomic) NSInteger idade;
@property (nonatomic) BOOL ativo;

// unsafe_unretained: como weak mas não fica nil
@property (nonatomic, unsafe_unretained) id alvo;

O copy cria uma cópia do valor (recomendado para NSString e blocos). O assign é para tipos primitivos. O unsafe_unretained é como weak mas não fica nil — evita-o se possível.

weak/strong dance em blocos
// Evitar retain cycle em blocos:
__weak typeof(self) weakSelf = self;

[self completarComBloco:^{
    __strong typeof(weakSelf) strongSelf = weakSelf;
    if (!strongSelf) return;
    [strongSelf fazerAlgo];
}];

Em blocos que capturam self, usa-se o padrão weak/strong dance: cria-se um weakSelf para evitar o ciclo e, dentro do bloco, um strongSelf temporário para garantir que existe durante a execução.

Properties
@interface Pessoa : NSObject
@property (nonatomic, strong) NSString *nome;
@property (nonatomic) NSInteger idade;
@end

@implementation Pessoa
// Gera automaticamente:
// - getter:  -nome
// - setter:  -setNome:
// - ivar:    _nome
@end

// nonatomic: mais rápido (não thread-safe)

Uma @property gera automaticamente o getter, o setter e a variável de instância (_nome). O atributo nonatomic torna o acesso mais rápido, abdicando de thread-safety.

dealloc e cleanup
@implementation MinhaClasse

- (void)dealloc {
    // Remover observers:
    [[NSNotificationCenter defaultCenter]
        removeObserver:self];

    // Fechar recursos manuais:
    [_ficheiro close];
    // ARC trata do resto
}

@end

O método dealloc é chamado quando o objeto é libertado. Serve para remover observers e fechar recursos manuais. Com ARC, não se chama [super dealloc] nem se liberta memória manualmente.

Protocolos e Blocos


8 cards
Protocolos
// Definir protocolo:
@protocol Saudavel <NSObject>
- (void)exercitar:(NSInteger)minutos;
@end

// Adotar na classe:
@interface Pessoa : NSObject <Saudavel>
@end

@implementation Pessoa
- (void)exercitar:(NSInteger)minutos {
    NSLog(@"Exercitou %ld min", (long)minutos);
}
@end

Um @protocol define um contrato de métodos (como uma interface). A classe adota-o entre < > na @interface e implementa os métodos exigidos.

Blocos como parâmetro
// Método que recebe bloco:
- (void)descarregar:(NSURL *)url
    completion:(void (^)(BOOL sucesso))handler {
    // ... trabalho assíncrono ...
    handler(YES);
}

// Chamar:
[self descarregar:url completion:^(BOOL ok) {
    if (ok) NSLog(@"Sucesso!");
}];

Os blocos são frequentemente passados como parâmetros de métodos (callbacks). A sintaxe void (^)(BOOL) indica um bloco que recebe BOOL e não devolve nada.

@required e @optional
@protocol Saudavel <NSObject>
@required
- (void)exercitar:(NSInteger)minutos;

@optional
- (void)meditar;
@end

// Verificar antes de chamar opcional:
if ([pessoa respondsToSelector:@selector(meditar)]) {
    [pessoa meditar];
}

Os métodos @required são obrigatórios (por omissão); os @optional não. Antes de chamar um método opcional, verifica-se com respondsToSelector: para evitar crashes.

typedef de blocos
// typedef para legibilidade:
typedef void (^CompletionHandler)(BOOL sucesso, NSError *erro);

// Usar em property:
@property (nonatomic, copy) CompletionHandler onCompletar;

// Usar em método:
- (void)descarregar:(NSURL *)url
    completion:(CompletionHandler)handler {
    handler(YES, nil);
}

Um typedef dá um nome legível a um tipo de bloco complexo, facilitando a reutilização em properties e assinaturas de métodos. A property de bloco deve ser copy.

Delegates
@interface MinhaView : NSObject
@property (nonatomic, weak) id<MeuDelegate> delegate;
@end

@implementation MinhaView
- (void)selecionarLinha:(NSInteger)row {
    [self.delegate didSelectRow:row];
}
@end

// delegate é weak para evitar retain cycle

O padrão delegate permite que um objeto comunique eventos a outro. A property delegate é weak (para evitar ciclos) e tipada com o protocolo que define os callbacks.

respondsToSelector
// Verificar se objeto responde a método:
if ([objeto respondsToSelector:@selector(meditar)]) {
    [objeto meditar];
}

// @selector cria um seletor (nome de método)
SEL s = @selector(length);
[string performSelector:s];

// Útil para métodos opcionais de protocolos

O respondsToSelector: verifica em runtime se um objeto implementa um método, evitando crashes com métodos opcionais. O @selector() representa o nome de um método como valor.

Blocos
// Declarar e usar bloco:
void (^meuBloco)(void) = ^{
    NSLog(@"Olá do bloco!");
};
meuBloco();

// Com parâmetros e retorno:
NSInteger (^soma)(NSInteger, NSInteger) =
    ^NSInteger(NSInteger a, NSInteger b) {
    return a + b;
};
NSLog(@"%ld", (long)soma(3, 4));   // 7

Um bloco é uma closure: código que captura o contexto. Declara-se com ^. Pode ter parâmetros e retorno, e é muito usado para callbacks e código assíncrono.

conformsToProtocol
// Verificar se adota um protocolo:
if ([objeto conformsToProtocol:@protocol(Saudavel)]) {
    id<Saudavel> s = (id<Saudavel>)objeto;
    [s exercitar:30];
}

// isKindOfClass: verifica a classe
if ([objeto isKindOfClass:[Pessoa class]]) {
    Pessoa *p = (Pessoa *)objeto;
}

O conformsToProtocol: verifica se um objeto adota um protocolo, permitindo um cast seguro. O isKindOfClass: faz o mesmo para a classe. Ambos são verificações de tipo em runtime.

Avançado


8 cards
Categorias
// NSString+Utils.h
@interface NSString (Utils)
- (BOOL)contemDigito;
@end

// NSString+Utils.m
@implementation NSString (Utils)
- (BOOL)contemDigito {
    NSCharacterSet *ds =
        [NSCharacterSet decimalDigitCharacterSet];
    return [self rangeOfCharacterFromSet:ds]
        .location != NSNotFound;
}
@end

Uma categoria adiciona métodos a uma classe existente sem a subclassificar, mesmo classes do sistema como NSString. O nome fica entre parênteses: NSString (Utils).

GCD (Grand Central Dispatch)
// Background + Main thread:
dispatch_async(dispatch_get_global_queue(
    DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    // Trabalho pesado aqui
    NSArray *dados = [self processar];

    dispatch_async(dispatch_get_main_queue(), ^{
        // Atualizar UI aqui
        self.label.text = @"Feito";
    });
});

O GCD gere concorrência com filas. O dispatch_async para uma fila global corre trabalho em background; depois volta à main queue para atualizar a UI (obrigatório no iOS).

Extensões de classe
// Pessoa.m (no topo, antes da @implementation)
@interface Pessoa ()
// Properties privadas:
@property (nonatomic) NSInteger contador;
- (void)metodoInterno;
@end

@implementation Pessoa
- (void)metodoInterno {
    self.contador++;
}
@end

Uma class extension (categoria anónima, ()) declara properties e métodos privados no ficheiro .m. Só é visível dentro da implementação, ao contrário das categorias nomeadas.

dispatch_after
// Executar após um atraso:
dispatch_after(
    dispatch_time(DISPATCH_TIME_NOW,
        2 * NSEC_PER_SEC),
    dispatch_get_main_queue(), ^{
    NSLog(@"Após 2 segundos");
});

// dispatch_once: corre só uma vez (singleton)
static dispatch_once_t once;
dispatch_once(&once, ^{
    instancia = [[self alloc] init];
});

O dispatch_after agenda um bloco com atraso (em nanossegundos). O dispatch_once garante que um bloco corre apenas uma vez, sendo o padrão clássico para implementar singletons.

KVC (Key-Value Coding)
// Aceder a properties por nome (string):
NSString *nome = [pessoa valueForKey:@"nome"];
[pessoa setValue:@"Ana" forKey:@"nome"];

// Percorrer coleções:
NSArray *nomes = [pessoas valueForKey:@"nome"];

// Agregação:
NSNumber *max = [numeros valueForKeyPath:@"@max.self"];

O KVC permite aceder a properties por nome (string) com valueForKey: e setValue:forKey:. O valueForKeyPath: suporta operadores de agregação como @max.

NSError
// Padrão NSError (passado por referência):
NSError *erro = nil;
NSString *conteudo = [NSString
    stringWithContentsOfFile:@"/tmp/f.txt"
    encoding:NSUTF8StringEncoding
    error:&erro];

if (erro) {
    NSLog(@"Erro: %@", erro.localizedDescription);
    NSLog(@"Código: %ld", (long)erro.code);
}

Em Objective-C, os erros usam o padrão NSError passado por referência (&erro). Se o método falhar, preenche o erro; verifica-se se é nil antes de o usar.

KVO (Key-Value Observing)
// Observar mudança numa property:
[pessoa addObserver:self
    forKeyPath:@"nome"
    options:NSKeyValueObservingOptionNew
    context:NULL];

// Callback chamado na mudança:
- (void)observeValueForKeyPath:(NSString *)keyPath
    ofObject:(id)object
    change:(NSDictionary *)change
    context:(void *)context {
    NSLog(@"Novo: %@", change[NSKeyValueChangeNewKey]);
}

O KVO notifica automaticamente quando uma property muda. Regista-se com addObserver:forKeyPath: e recebe-se a notificação em observeValueForKeyPath: com o valor novo.

NSNotificationCenter
// Subscrever notificação:
[[NSNotificationCenter defaultCenter]
    addObserver:self
    selector:@selector(dadosRecebidos:)
    name:@"DadosAtualizados"
    object:nil];

// Enviar notificação:
[[NSNotificationCenter defaultCenter]
    postNotificationName:@"DadosAtualizados"
    object:nil];

// Remover (no dealloc)

O NSNotificationCenter é um sistema de publicação/subscrição global. Subscreve-se com addObserver:selector:name: e envia-se com 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: interface (só iOS)
#import <UIKit/UIKit.h>
// UIView, UIViewController, UILabel...

// AppKit: interface (só macOS)
#import <AppKit/AppKit.h>

O framework Foundation fornece os tipos base (strings, coleções, datas) em todas as plataformas. O UIKit (iOS) e o AppKit (macOS) fornecem a interface gráfica.

NSURL e NSURLSession
NSURL *url = [NSURL URLWithString:
    @"https://api.exemplo.com/dados"];

NSURLSession *sessao = [NSURLSession sharedSession];

NSURLSessionDataTask *task = [sessao
    dataTaskWithURL:url
    completionHandler:^(NSData *dados,
        NSURLResponse *resp, NSError *erro) {
    if (!erro) {
        // processar dados
    }
}];
[task resume];

O NSURL representa um endereço. O NSURLSession faz pedidos de rede assíncronos; o dataTaskWithURL: cria a tarefa e o completionHandler recebe os dados. Chama-se resume para iniciar.

NSUserDefaults
NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];

// Guardar:
[prefs setObject:@"Ana" forKey:@"nome"];
[prefs setInteger:30 forKey:@"idade"];
[prefs setBool:YES forKey:@"ativo"];
[prefs synchronize];

// Ler:
NSString *nome = [prefs stringForKey:@"nome"];
NSInteger idade = [prefs integerForKey:@"idade"];

O NSUserDefaults guarda pequenas preferências persistentes (definições). Usa setObject:forKey: para guardar e objectForKey: (ou variantes tipadas) para ler.

NSDate e NSDateFormatter
// Data atual:
NSDate *agora = [NSDate date];

// Formatar para string:
NSDateFormatter *fmt = [[NSDateFormatter alloc] init];
[fmt setDateFormat:@"yyyy-MM-dd HH:mm"];
NSString *texto = [fmt stringFromDate:agora];

// Parse de string para data:
NSDate *data = [fmt dateFromString:@"2024-01-15 10:30"];

O NSDate representa um instante. O NSDateFormatter converte entre datas e strings com um formato definido por setDateFormat: (ex.: yyyy-MM-dd).

NSFileManager
NSFileManager *fm = [NSFileManager defaultManager];

// Verificar existência:
BOOL existe = [fm fileExistsAtPath:@"/tmp/f.txt"];

// Criar pasta:
[fm createDirectoryAtPath:@"/tmp/pasta"
    withIntermediateDirectories:YES
    attributes:nil error:nil];

// Listar conteúdo:
NSArray *itens = [fm contentsOfDirectoryAtPath:@"/tmp"
    error:nil];

O NSFileManager gere o sistema de ficheiros: verifica existência com fileExistsAtPath:, cria pastas com createDirectoryAtPath: e lista conteúdo com contentsOfDirectoryAtPath:.

JSON (NSJSONSerialization)
// JSON -> objetos:
NSDictionary *json = [NSJSONSerialization
    JSONObjectWithData:dados
    options:0 error:&erro];

// objetos -> JSON:
NSData *saida = [NSJSONSerialization
    dataWithJSONObject:dict
    options:NSJSONWritingPrettyPrinted
    error:&erro];

NSString *texto = [[NSString alloc]
    initWithData:saida encoding:NSUTF8StringEncoding];

O NSJSONSerialization converte entre JSON e objetos Foundation. JSONObjectWithData: faz parse de NSData para dicionários/arrays; dataWithJSONObject: faz o inverso.

Ferramentas e Padrões


6 cards
Estrutura .h / .m
// Pessoa.h (interface pública)
#import <Foundation/Foundation.h>
@interface Pessoa : NSObject
@property (nonatomic, copy) NSString *nome;
- (NSString *)saudacao;
@end

// Pessoa.m (implementação)
#import "Pessoa.h"
@implementation Pessoa
- (NSString *)saudacao {
    return [NSString stringWithFormat:@"Olá, %@", self.nome];
}
@end

Cada classe divide-se em dois ficheiros: o .h (header) declara a interface pública e o .m (implementation) contém o código. O .m importa o seu próprio .h.

Modern Objective-C
// Literals (moderno):
NSArray *arr = @[@1, @2, @3];
NSNumber *n = @42;

// Subscripting:
id obj = arr[0];
dict[@"chave"] = valor;

// Instancetype em vez de id:
+ (instancetype)novo;

// NS_ENUM / NS_OPTIONS:
typedef NS_ENUM(NSInteger, Cor) { CorAzul };

O Objective-C moderno usa literals (@[], @42), subscripting (arr[0]), instancetype e NS_ENUM. Estas features tornam o código mais conciso e seguro.

#import e #include
// #import: inclui uma só vez (recomendado)
#import "Pessoa.h"          // header local
#import <Foundation/Foundation.h>  // framework

// #include: pode incluir várias vezes (C)
#include "utils.h"

// @class: forward declaration (no .h)
@class Pessoa;   // evita importar o header completo

O #import inclui um header garantindo que só é lido uma vez (preferível ao #include). O @class é uma forward declaration usada no .h para evitar dependências circulares.

Boas práticas
// 1. Properties em vez de ivars públicos
@property (nonatomic, copy) NSString *nome;

// 2. NSString como copy
// 3. delegate como weak
// 4. nil checks em opcionais
if ([obj respondsToSelector:@selector(m)]) { }

// 5. Erros via NSError**, não exceções
// 6. Imutabilidade por omissão
//    (NSArray, não NSMutableArray)

Boas práticas: usa properties, marca NSString como copy e delegate como weak, trata erros via NSError (não exceções) e prefere coleções imutáveis por omissão.

Compilação
# Compilar com clang:
clang -framework Foundation main.m -o programa

# Executar:
./programa

# No Xcode:
# Cmd+B  -> build
# Cmd+R  -> build & run

# Ficheiros compilados:
# .m  -> .o (object) -> executável

O compilador clang transforma .m em executável; a flag -framework Foundation liga o framework. No Xcode, Cmd+B compila e Cmd+R executa.

Prefixos e convenções
// Prefixos de 2-3 letras (evitam conflitos):
@interface ABCPessoa : NSObject
@end

// Convenções de nomes:
// Classes:    PascalCase (ABCPessoa)
// Métodos:    camelCase (contarItens)
// Properties: camelCase (primeiroNome)
// Constantes: kPrefixo (kMaxTentativas)

static const NSInteger kMaxTentativas = 3;

As classes usam um prefixo de 2-3 letras (ex.: NS, UI, ABC) para evitar conflitos de nomes. Classes em PascalCase, métodos e properties em camelCase, constantes com k.