Fundamentos da Estrutura Interna de Classes no Objective-C

Modelo de Objetos e Alocação de Memória

O runtime do Objective-C gerencia todas as instâncias e definições de classe através de estruturas de memória estritamente padronizadas. No nível mais básico, toda instância de um objeto é representada pela estrutura objc_object, que contém exclusivamente um campo isa. A própria classe é mapeada como um objeto especial, definido por objc_class, que herda de objc_object e estende seu layout com campos de despacho, cache e ponteiros de dados.

struct objc_object {
    Class _Nonnull isa OBJC_ISA_AVAILABILITY;
};

struct objc_class : objc_object {
    Class superclass;
    cache_t cache;
    class_data_bits_t bits;
};

Navegação via Ponteiro isa e Cadeias de Herança

Em arquiteturas de 64 bits (ARM64/x86_64), o campo isa não armazena um ponteiro bruto. Ele utiliza uma técnica de bit packing, onde parte dos bits representa o endereço da classe ou metaclass e outra parte carrega flags de controle do runtime. Para recuperar o endereço real, o runtime aplica uma operação de máscara (ISA_MASK) seguida de um deslocamento à esquerda.

A cadeia de navegação segue um padrão rígido:

  • A instância aponta seu isa para a classe correspondente.
  • A classe aponta seu isa para a metaclass.
  • A metaclass aponta seu isa para a <strong>root metaclass</strong> (geralmente NSObject).
  • A root metaclass aponta seu isa para si mesma, fechando o ciclo de ponteiros.

Paralelamente, existe a cadeia de herança (superclass). Uma subclasse herda da classe pai, que herda da classe raiz. A classe raiz (NSObject) possui sua superclass definida como nil. Na dimensão das metaclasse, a sub-metaclasse herda da metaclasce da classe pai, e a root metaclass herda diretamente da classe raiz NSObject. É importante ressaltar que instâncias isoladas não participam de herança; essa hierarquia existe exclusivamente entre estruturas de tipo.

Essa topologia define onde os métodos são armazenados: métodos de instância residem no armazenamento de dados da classe, enquanto métodos de classe (estáticos) são persistidos no armazenamento de dados da respectiva metaclass.

Estrutura de Cache e Otimização

O campo cache (cache_t) acelera a resolução de métodos armazenando pares de seletores e implementações frequentemente acessados. Sua estrutura varia conforme a estratégia de armazenamento da máscara, mas em sistemas __LP64__, o alinhamento de memória garante um tamanho fixo de 16 bytes. A estrutura combina ponteiros para buckets, máscaras de tamanho e indicadores de ocupação, minimizando o impacto de alinhamento.

Gerenciamento de Dados via bits: ro, rw e rw_ext

O campo bits em objc_class é um wrapper que, após deslocamento de 32 bytes em relação ao endereço base da classe, aponta para estruturas de gerenciamento de dados. O runtime evoluiu para separar informações estáticas (definidas em tempo de compilação) das dinâmicas (modificáveis em execução).

struct class_ro_t {
    uint32_t flags;
    uint32_t instanceStart;
    uint32_t instanceSize;
#ifdef __LP64__
    uint32_t reserved;
#endif
    const uint8_t *ivarLayout;
    const char *name;
    method_list_t *baseMethodList;
    protocol_list_t *baseProtocols;
    const ivar_list_t *ivars;
    const uint8_t *weakIvarLayout;
    property_list_t *baseProperties;
};

struct class_rw_ext_t {
    method_list_t *attachedMethods;
    protocol_list_t *attachedProtocols;
    property_list_t *attachedProperties;
    ivar_list_t *extendedIvars;
};

struct class_rw_t {
    uint32_t flags;
    uint16_t witness;
    explicit_atomic<uintptr_t> ro_or_rw_ext;
    Class firstSubclass;
    Class nextSiblingClass;

    const method_array_t methods() const {
        auto container = get_ro_or_rwe();
        if (container.is<class_rw_ext_t *>()) {
            return method_array_t{container.get<class_rw_ext_t *>()->attachedMethods};
        }
        return method_array_t{container.get<const class_ro_t *>()->baseMethodList};
    }

    const property_array_t properties() const {
        auto container = get_ro_or_rwe();
        if (container.is<class_rw_ext_t *>()) {
            return property_array_t{container.get<class_rw_ext_t *>()->attachedProperties};
        }
        return property_array_t{container.get<const class_ro_t *>()->baseProperties};
    }
};

class_ro_t (Read-Only) é alocada durante a compilação. Contém o layoutt base, nome, tamanho, variáveis de instância (ivars), e as listas iniciais de métodos e propriedades. O acesso à lista de variáveis de instância tipicamente exige um deslocamento de 48 bytes do início da estrutura em ambientes de 64 bits.

Quando uma classe é carregada no runtime (realizeClass), o sistema aloca um class_rw_t (Read-Write). Essa estrutura permite modificações em tempo de execução, como injeção de métodos via categorias ou manipulação direta da API. Para otimizar a memória, já que a maioria das classes nunca é alterada dinamicamente, o runtime extrai apenas os componentes mutáveis para um class_rw_ext_t (Read-Write Extension). Isso reduz drasticamente a pegada de memória das classes que não utilizam categorias ou métodos dinâmicos.

Exemplo de Inspeção de Estrutura

Para validar a distribuição de dados, considere a seguinte definição de classes:

@interface VeiculoBase : NSObject
{
    NSString *placaIdentificadora;
}
@property (nonatomic, copy) NSString *modeloFabrica;
- (void)acelerarMotor;
+ (void)registrarRecall;
@end

@implementation VeiculoBase
- (void)acelerarMotor {}
+ (void)registrarRecall {}
@end

@interface CarroEletrico : VeiculoBase
@end

@implementation CarroEletrico
@end

Ao inspecionar o bits de VeiculoBase, o método methods() retornará uma matriz contendo acelerarMotor e os accessors sintetizados para modeloFabrica. A variável placaIdentificadora não aparece na lista de métodos; ela está armazenada exclusivamente no campo ivars dentro de class_ro_t. Se um método adicional for injetado via categoria, ele será alocado na attachedMethods de class_rw_ext_t, enquanto o método de classe registrarRecall será localizado ao inspecionar o bits da metaclass de VeiculoBase, comprovando a separação física entre métodos de instância e de classe.

Tags: objective-c-runtime isa-pointer memory-layout metaclasse class_rw_t

Publicado em 9-22 18:06