Protocolos (Protocol)
Em Objective-C, os protocolos são utilizados para definir um contrato de métodos que outras classes podem adotar, estabelecendo um conjunto de comportamentos padronizados. Diferente de algumas linguagens orientadas a objetos que utilizam a palavra-chave interface para este fim, no Objective-C o termo @interface é estritamente reservado para a declaração de classes. Para definir contratos, utilizamos a diretiva @protocol.
Abaixo, definimos um protocolo chamado DataProcessing e uma classe JSONParser que assume o compromisso de implementar seus métodos.
// DataProcessing.h
#import <Foundation/Foundation.h>
@protocol DataProcessing <NSObject>
- (void)processData:(NSString *)data;
@end
// JSONParser.h
#import <Foundation/Foundation.h>
#import "DataProcessing.h"
@interface JSONParser : NSObject <DataProcessing>
@end
// JSONParser.m
#import "JSONParser.h"
@implementation JSONParser
- (void)processData:(NSString *)data {
NSLog(@"Processando payload de dados: %@", data);
}
@end
Categorias (Category)
Categorias são um recurso poderoso que permite adicionar novos métodos a uma classe existente sem a necessidade de criar uma subclasse ou alterar o código-fonte original. Isso é particularmente útil para estender classes nativas do framwork Foundation. Por exemplo, podemos criar uma extensão para a classe NSString a fim de adicionar uma funcionalidade de inversão de texto.
// NSString+Reverse.h
#import <Foundation/Foundation.h>
@interface NSString (ReverseExtension)
- (NSString *)reversedString;
@end
// NSString+Reverse.m
#import "NSString+Reverse.h"
@implementation NSString (ReverseExtension)
- (NSString *)reversedString {
NSMutableString *reversed = [NSMutableString stringWithCapacity:self.length];
for (NSUInteger i = self.length; i > 0; i--) {
[reversed appendFormat:@"%C", [self characterAtIndex:(i - 1)]];
}
return reversed;
}
@end
// main.m
#import <Foundation/Foundation.h>
#import "NSString+Reverse.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSString *originalText = @"Objective-C";
NSLog(@"Texto original: %@", originalText);
NSString *invertedText = [originalText reversedString];
NSLog(@"Texto invertido: %@", invertedText);
}
return 0;
}
Blocos (Block)
Blocos representam a implementação de closures no Objective-C. Eles permitem que trechos de código sejam encapsulados, passados como argumentos, armazenados em variáveis e executados posteriormente. Isso é fundamental para operações assíncronas, callbacks e manipulação de coleções, funcionando de maneira similar aos delegados anônimos ou expressões lambda em outras linguagens.
A seguir, demonstramos o uso de um bloco para gerenciar o callback de uma tarefa assíncrona simulada.
// TaskManager.h
#import <Foundation/Foundation.h>
typedef void (^CompletionHandler)(NSString *result, NSError *error);
@interface TaskManager : NSObject
- (void)executeTaskWithCompletion:(CompletionHandler)handler;
@end
// TaskManager.m
#import "TaskManager.h"
@implementation TaskManager
- (void)executeTaskWithCompletion:(CompletionHandler)handler {
// Simulação de processamento assíncrono
NSString *mockResult = @"Dados processados com sucesso";
if (handler) {
handler(mockResult, nil);
}
}
@end
// main.m
#import <Foundation/Foundation.h>
#import "TaskManager.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
TaskManager *manager = [[TaskManager alloc] init];
[manager executeTaskWithCompletion:^(NSString *result, NSError *error) {
if (error) {
NSLog(@"Falha na execução: %@", error.localizedDescription);
} else {
NSLog(@"Sucesso: %@", result);
}
}];
}
return 0;
}
Ao trabalhar com blocos, é essencial compreender as seguintes regras de sintaxe e comportamento:
- Declaração de Tipo: A estrutura básica é
TipoDeRetorno (^nomeDaVariavel)(ListaDeParametros). - Uso de typedef: Para melhorar a legibilidade, recomenda-se definir tipos personalizados para blocos complexos, como em
typedef void (^CompletionHandler)(NSString *, NSError *);. - Implementação: A sintaxe para instanciar o bloco é
^(ListaDeParametros) { corpo do bloco }. - Captura de Variáveis: Blocos podem ler variáveis definidas em seu escopo externo. No entanto, para modificar o valor de uma variável externa, é obrigatório declará-la utilizando o modificador
__block.