Cheatsheet Objective-C
Linguagem orientada a objetos da Apple para macOS e iOS
Objective-C
Básico
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
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 paiPara 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;
}
@endA @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;
}
@endA 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;
}
@endO 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
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 valoresO 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
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
}
@endO 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
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);
}
@endUm @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 cycleO 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 protocolosO 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)); // 7Um 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
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;
}
@endUma 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++;
}
@endUma 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
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
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];
}
@endCada 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.