Explorando Modificadores de Propriedade em Objective-C

Em Objective-C, os modificadores de propriedade são essenciais para gerenciar como as propriedades de um objeto se comportam, especialmente em relação à memória, acesso, segurança de thread e outros aspectos. Vamos detalhar os modificadores mais comuns.

Modificadores de Gerenciamento de Memória

Esses modificadores controlam a propriedade e o ciclo de vida dos objetos referenciados por uma propriedade.

strong

O modificador strong indica que a propriedade mantém uma forte referência ao objeto. Isso significa que o objeto referenciado terá seu contador de referência incrementado e não será desalocado enquanto a propriedade mantiver essa referência. É o modificador padrão e é usado quando a propriedade "possui" o objeto.

@interface Configuracao : NSObject
@property (nonatomic, strong) NSString *nomeUsuario;
@end

@implementation Configuracao
@end

Neste exemplo, nomeUsuario é um strong. Quando um valor é atribuído a ele, o contador de referência do objeto NSString é incrementado. O objeto NSString só será liberado quando não houver mais referências strong apontando para ele.

weak

O modificador weak indica uma referência fraca. Ele não incrementa o contador de referência do objeto. Isso é crucial para evitar ciclos de retenção, onde dois objetos se referenciam mutuamente com strong, impedindo que ambos sejam liberados. Quando o objeto referenciado é liberado, as referências weak a ele são automaticamente definidas como nil.

@interface Pedido : NSObject
@property (nonatomic, strong) NSMutableArray *itens;
@end

@interface ItemPedido : NSObject
@property (nonatomic, weak) Pedido *pedidoPai;
@end

@implementation Pedido
@end

@implementation ItemPedido
@end

Aqui, pedidoPai em ItemPedido é weak. Mesmo que um ItemPedido tenha um Pedido como pai, essa referência não impede que o Pedido seja liberado. Se o Pedido for liberado, itemPedido.pedidoPai se tornará nil automaticamente.

unsafe_unretained

Semelhante a weak, unsafe_unretained não incrementa o contador de referência. A grande diferença é que, quando o objeto referenciado é liberado, a referência unsafe_unretained não é definida como nil. Em vez disso, ela continua apontando para a área de memória liberada, tornando-se um ponteiro pendente (dangling pointer). Acessar essa referência pode levar a um comportamento indefinido e falhas no aplicativo. É raramente usado e geralmente desaconselhado em favor de weak.

@interface CacheItem : NSObject
@property (nonatomic, unsafe_unretained) id dados;
@end

@implementation CacheItem
@end

Se o objeto atribuído a dados for liberado, cacheItem.dados permanecerá apontando para a memória liberada, representando um risco.

copy

O modificador copy instrui o compilador a criar uma cópia do objeto atribuído. Isso é especialmente importante para tipos de objetos mutáveis (como NSMutableString, NSMutableArray) que são atribuídos a propriedades declaradas como imutáveis (NSString, NSArray). Garantir que as alterações no objeto original não afetem a cópia mantida pela propriedade.

@interface ConfiguracaoApp : NSObject
@property (nonatomic, copy) NSString *tituloApp;
@end

@implementation ConfiguracaoApp
@end

Se um NSMutableString for atribuído à propriedade tituloApp (que é copy), uma cópia desse NSMutableString será criada. Alterações posteriores no NSMutableString original não afetarão o valor de configuracaoApp.tituloApp.

__weak

__weak é a versão de variável de escopo local ou membro para o modificador weak. Ele cria uma referência fraca que não afeta o contador de referência do objeto e é automaticamente definida como nil quando o objeto referenciado é liberado. É amplamente utilizado em blocos para evitar ciclos de retenção.

__weak typeof(self) weakSelf = self;
[UIView animateWithDuration:0.3 animations:^{
    // Acessando a propriedade do objeto fraco
    weakSelf.view.alpha = 0.0;
}];

Neste exemplo, weakSelf é uma referência fraca à instância atual. Isso impede que o bloco de animação crie um ciclo de retenção com a instância do controlador de visualização, permitindo que self seja liberado corretamente quando necessário.

__strong

__strong é a versão de variável de escopo local ou membro para o modificador strong. Ele representa a posse direta de um objeto, incrementando seu contador de referência. Em um ambiente ARC, é o modifiacdor padrão para variáveis locais se nenhum outro modificador de posse for especificado.

void processarObjeto() {
    __strong MyObject *objeto = [[MyObject alloc] init];
    // objeto é forte; o objeto MyObject não será liberado enquanto estiver em escopo
    [objeto fazerAlgo];
} // objeto sai de escopo, o contador de referência é decrementado.

O uso de __strong garante que o objeto permaneça válido enquanto a referência existir.

__unsafe_unretained

__unsafe_unretained é a versão de variável de escopo local ou membro para o modificador unsafe_unretained. Assim como seu equivalente de propriedade, ele não afeta o contador de referência e não zera automaticamente quando o objeto é liberado, tornando-se um ponteiro pendente.

__autoreleasing

__autoreleasing é um modificador de posse usado principalmente para parâmetros de saída em métodos que retornam erros. Ele coloca o objeto em um pool de autorelease, garantindo que ele não seja liberado imediatamente, mas sim no final do loop de eventos atual. O compilador ARC geralmente insere isso automaticamente quando necessário, especialmente com ponteiros para objetos em parâmetros de saída.

- (BOOL)carregarDados:(NSError * __autoreleasing *)erro {
    if (/* falha */) {
        if (erro) {
            *erro = [NSError errorWithDomain:@"MeuDominio" code:1 userInfo:nil];
        }
        return NO;
    }
    return YES;
}

O objeto NSError retornado e atribuído a *erro é gerenciado pelo pool de autorelease.

assign

assign é usado principalmente para tipos de dados primitivos (como NSInteger, CGFloat) ou ponteiros C. Ele simplesmente atribui o valor sem gerenciar a posse ou o ciclo de vida do objeto. Para objetos Objective-C, usar assign pode levar a ponteiros pendentes se o objeto for liberado enquanto a propriedade ainda o referencia.

@interface ConfiguracaoBasica : NSObject
@property (nonatomic, assign) NSInteger nivelDificuldade;
@end

nivelDificuldade, sendo um tipo primitivo, é perfeitamente adequado para assign.

retain

retain era o modificador de gerenciamento de memória padrão em Objective-C com contagem manual de referências (MRC). Ele instruía o setter a reter (incrementar o contador de referência) o novo objeto e liberar o antigo. Em ARC, strong é o substituto preferido e mais seguro.

Modificadores de Acesso à Propriedade

Esses modificadores controlam como uma propriedade pode ser acessada e modificada.

readonly

Uma propriedade declarada como readonly possui apenas um método getter e não tem um método setter gerado automaticamente. Seu valor só pode ser definido dentro da classe, geralmente durante a inicialização.

// Em MyObject.h
@interface MyObject : NSObject
@property (nonatomic, readonly) NSDate *dataCriacao;
@end

// Em MyObject.m
@interface MyObject()
@property (nonatomic, readwrite) NSDate *dataCriacao; // Para permitir escrita interna
@end

@implementation MyObject
- (instancetype)init {
    if (self = [super init]) {
        _dataCriacao = [NSDate date]; // Definido internamente
    }
    return self;
}
@end

No cabeçalho, dataCriacao é readonly. O acesso direto para escrita fora da classe é impedido.

readwrite

readwrite é o modificador padrão, que gera tanto um método getter quanto um método setter. Isso permite que a propriedade seja lida e escrita livremente de qualquer lugar onde o objeto seja acessível.

getter=nomeGetter

Permite especificar um nome personalizado para o método getter da propriedade. É comumente usado para propriedades booleanas, onde o getter pode começar com "is" em vez de "get".

@interface Tarefa : NSObject
@property (nonatomic, getter=estaCompleta) BOOL completa;
@end

Para acessar o valor, você usaria [tarefa estaCompleta] em vez de tarefa.completa (embora o acesso por ponto tarefa.completa ainda funcione internamente).

setter=nomeSetter:

Permite especificar um nome personalizado para o método setter da propriedade. O nome deve terminar com dois pontos (:).

@interface Usuario : NSObject
@property (nonatomic, setter=definirNomeUsuario:) NSString *nomeUsuario;
@end

Para definir o valor, você usaria [usuario definirNomeUsuario:@"Alice"].

dynamic

O modificador dynamic informa ao compilador que os métodos getter e setter para esta propriedade serão fornecidos dinamicamente em tempo de execução, em vez de serem gerados automaticamente pelo compilador em tempo de compilação. Isso é frequentemente usado em conjunto com frameworks como Core Data, onde os setters são implementados em tempo de execução.

@interface ModeloDados : NSObject
@property (nonatomic, readonly, dynamic) NSString *identificadorUnico;
@end

@implementation ModeloDados
@dynamic identificadorUnico; // Indica que o compilador não deve gerar o setter/getter

- (NSString *)identificadorUnico {
    // Lógica para obter ou gerar o identificador em tempo de execução
    return @"XYZ123";
}
@end

Modificadores de Segurança de Thread

Esses modificadores determinam se o acesso à propriedade é atômico (thread-safe).

nonatomic

Indica que o acesso à propriedade não é atômico e, portanto, não é thread-safe por padrão. O acesso é mais rápido, mas pode levar a condições de corrida se a propriedade for acessada por múltiplos threads simultaneamente sem sincronização adicional.

@property (nonatomic, strong) NSMutableArray *listaLogs;

atomic

É o modificador padrão. Garante que os acessos getter e setter sejam atômicos, ou seja, indivisíveis. Isso significa que uma operação de leitura ou escrita nunca será interrompida por outra operação no mesmo getter/setter. No entanto, atomic não garante a atomicidade de operações compostas (ler, modificar, escrever).

@property (atomic, strong) NSNumber *contadorAtivo;
// Ou simplesmente:
// @property NSNumber *contadorAtivo;

Para operações compostas, como incrementar um contador, ainda é necessária sincronização explícita usando locks ou outras primitivas de concorrência.

Outros Modificadores

__kindof

__kindof é um especificador de tipo usado com genéricos para indicar que um tipo é uma instância de uma classe especificada ou de qualquer uma de suas subclasses. Isso melhora a segurança de tipo em cenários genéricos.

@interface Caixa<ObjectType : __kindof Animal *> : NSObject
@property (nonatomic, readonly) ObjectType conteudo;
@end

Aqui, conteudo pode ser um Animal ou qualquer subclasse de Animal (como Dog, Cat).

nullable

Indica que um valor (parâmetro, retorno, propriedade) pode ser nil. Auxilia na detecção de erros em tempo de compilação.

- (nullable NSString *)buscarNomePorID:(NSInteger)idUsuario;

nonnull

Indica que um valor não deve ser nil. O compilador emitirá um aviso se um valor nil for atribuído ou retornado onde nonnull é esperado.

@property (nonatomic, nonnull) NSString *identificador;

null_unspecified

Um especificador de nulidade que indica que o compilador não tem informação sobre se um valor pode ser nil ou não. É usado principalmente para interoperabilidade com Swift.

null_resettable

Um modificador de nulidade que indica que um getter nunca retorna nil, mas um setter pode aceitar nil. Quando nil é definido, o getter retorna um valor padrão.

@property (nonatomic, null_resettable) NSString *nomeConfiguracao;

Se nomeConfiguracao for definido como nil, o getter pode retornar uma string padrão como "Padrão".

Modificadores de Variável

__block

Usado para permitir que variáveis locais ou membros sejam modificdaas dentro de um bloco. Sem __block, variáveis capturadas por blocos são apenas para leitura.

__block int contadorGlobal = 0;
void (^meuBloco)() = ^{
    contadorGlobal++; // Modificando a variável externa
};

__unused

Indica ao compilador que uma variável ou parâmetro pode não ser utilizado, suprimindo avisos de "variável não utilizada".

void funcaoComParametroNaoUsado(__unused int parametro) {
    // O parâmetro não é usado aqui.
}

Modificadores de Função e Método

__attribute__

Um modificador geral para fornecer informações adicionais ao compilador. Exemplos incluem deprecated (marca algo como obsoleto), nonnull (similar ao modificador de tipo), e visibility (controla a visibilidade de símbolos).

- (void)metodoAntigo __attribute__((deprecated("Use novoMetodo em vez disso.")));

API_AVAILABLE (Objective-C) / @available (Swift)

Esses são macros/palavras-chave que indicam em qual versão do sistema operacional uma API se tornou disponível. São usados para controle de compatibilidade entre diferentes versões de sistemas.

if (@available(iOS 13.0, *)) {
    // Código específico para iOS 13+
} else {
    // Código para versões anteriores
}

__deprecated

Similar a __attribute__((deprecated)), marca um elemento de código como obsoleto, emitindo um aviso do compilador quando ele é usado.

Tags: Objective-C ARC gerenciamento de memória Propriedades modificadores

Publicado em 8-2 21:05