@class vs. #import

É no meu entender que se deve usar uma declaração de class avançada no evento ClassA precisa include um header ClassB, e ClassB precisa include um header ClassA para evitar inclusões circulares. Eu também entendo que um #import é um simples ifndef para que uma inclusão só aconteça uma vez.

Minha pergunta é a seguinte: quando alguém usa o #import e quando usa o @class ? Às vezes, se eu usar uma declaração @class , vejo um aviso comum do compilador, como o seguinte:

warning: receiver 'FooController' is a forward class and corresponding @interface may not exist.

Realmente adoraria entender isso, em vez de apenas remover a @class antecipada de @class e lançar um #import em para silenciar os avisos que o compilador está me dando.

Se você vir este aviso:

aviso: receptor ‘MyCoolClass’ é uma class avançada e @interface correspondente pode não existir

você precisa #import o arquivo, mas pode fazer isso em seu arquivo de implementação (.m) e usar a declaração @class no seu arquivo de header.

@class não @class (geralmente) a necessidade de #import arquivos, apenas move o requisito para mais perto de onde a informação é útil.

Por exemplo

Se você disser @class MyCoolClass , o compilador sabe que pode ver algo como:

 MyCoolClass *myObject; 

Ele não precisa se preocupar com nada além de MyCoolClass é uma class válida, e deve reservar espaço para um ponteiro para ele (na verdade, apenas um ponteiro). Assim, no seu header, @class é suficiente 90% do tempo.

No entanto, se você precisar criar ou acessar os membros do myObject , será necessário informar ao compilador quais são esses methods. Neste ponto (presumivelmente no seu arquivo de implementação), você precisará #import "MyCoolClass.h" , para informar ao compilador informações adicionais além de “isto é uma class”.

Três regras simples:

  • Apenas # #import a superclass e adota protocolos em arquivos de header (arquivos .h ).
  • #import todas as classs e protocolos para os quais você envia mensagens em implementação (arquivos .m ).
  • Encaminhar declarações para todo o resto.

Se você enviar declaração nos arquivos de implementação, provavelmente fará algo errado.

Veja a documentação da Linguagem de programação C-Objective no ADC

Na seção Definindo uma class | Interface de class descreve por que isso é feito:

A diretiva @class minimiza a quantidade de código visto pelo compilador e pelo vinculador e, portanto, é a maneira mais simples de fornecer uma declaração de encaminhamento de um nome de class. Sendo simples, evita problemas potenciais que podem vir com a importação de arquivos que importam outros arquivos. Por exemplo, se uma class declara uma variável de instância de outra class e seus dois arquivos de interface importam um ao outro, nenhuma class pode ser compilada corretamente.

Eu espero que isso ajude.

Use uma declaração de encaminhamento no arquivo de header, se necessário, e #import os arquivos de header para quaisquer classs que você estiver usando na implementação. Em outras palavras, você sempre #import os arquivos que está usando em sua implementação e, se precisar fazer referência a uma class em seu arquivo de header, use também uma declaração de encaminhamento.

A exceção a isso é que você deve #import uma class ou protocolo formal que você está herdando no seu arquivo de header (nesse caso, você não precisaria importá-lo na implementação).

A prática comum é usar @class nos arquivos de header (mas você ainda precisa #importar a superclass) e #importar nos arquivos de implementação. Isso evitará inclusões circulares e simplesmente funcionará.

Outra vantagem: compilation rápida

Se você include um arquivo de header, qualquer alteração @class name com que o arquivo atual também seja compilado, mas esse não é o caso se o nome da class estiver incluído como @class name . Claro que você precisará include o header no arquivo de origem

Minha pergunta é essa. Quando alguém usa #import e quando usa @class?

Resposta simples: você #import ou #include quando há uma dependência física. Caso contrário, você usa declarações de encaminhamento ( @class MONClass , struct MONStruct , @protocol MONProtocol ).

Aqui estão alguns exemplos comuns de dependência física:

  • Qualquer valor C ou C ++ (um ponteiro ou referência não é uma dependência física). Se você tiver um CGPoint como um ivar ou propriedade, o compilador precisará ver a declaração de CGPoint .
  • Sua superclass.
  • Um método que você usa.

Às vezes, se eu usar uma declaração @class, vejo um aviso comum do compilador, como o seguinte: “warning: receiver ‘FooController’ é uma class forward e @interface correspondente pode não existir.”

O compilador é realmente muito tolerante a esse respeito. Ele soltará dicas (como a acima), mas você pode acabar com sua pilha facilmente se as ignorar e não #import corretamente. Embora deva (IMO), o compilador não impõe isso. No ARC, o compilador é mais rigoroso porque é responsável pela contagem de referência. O que acontece é que o compilador recorre a um padrão quando encontra um método desconhecido que você chama. Cada valor de retorno e parâmetro é assumido como id . Assim, você deve erradicar todos os avisos de suas bases de código, porque isso deve ser considerado dependência física. Isso é análogo a chamar uma function C que não está declarada. Com C, os parâmetros são assumidos como int .

A razão pela qual você favorece as declarações de encaminhamento é que você pode reduzir seus tempos de construção por fatores, porque há dependência mínima. Com as declarações de encaminhamento, o compilador vê que existe um nome e pode analisar e compilar corretamente o programa sem ver a declaração de class ou todas as suas dependencies quando não há dependência física. Construções limpas levam menos tempo. Construções incrementais levam menos tempo. Claro, você vai acabar gastando um pouco mais de tempo certificando-se de que todos os headers que você precisa estejam visíveis para cada tradução como consequência, mas isso compensa em tempos de compilation reduzidos rapidamente (supondo que seu projeto não seja pequeno).

Se você usar #import ou #include , estará trabalhando muito mais no compilador do que o necessário. Você também está introduzindo dependencies de header complexas. Você pode compará-lo a um algoritmo de força bruta. Quando você #import , você está arrastando toneladas de informações desnecessárias, o que requer muita memory, E / S de disco e CPU para analisar e compilar as fonts.

ObjC é bem próximo do ideal para uma linguagem baseada em C em relação à dependência, porque os tipos NSObject nunca são valores – os tipos NSObject são sempre pointers de referência. Assim, você pode se safar com tempos de compilation incrivelmente rápidos se estruturar as dependencies de seu programa adequadamente e avançar sempre que possível, pois há muito pouca dependência física necessária. Você também pode declarar propriedades nas extensões de class para minimizar ainda mais a dependência. Isso é um grande bônus para sistemas grandes – você saberia a diferença que faz se você já desenvolveu uma grande base de código C ++.

Portanto, minha recomendação é usar os forwards sempre que possível e, em seguida, #import onde há dependência física. Se você ver o aviso ou outro que implica dependência física – conserte todos eles. A correção é #import no seu arquivo de implementação.

À medida que você cria bibliotecas, provavelmente classificará algumas interfaces como um grupo; nesse caso, você deve #import a biblioteca em que a dependência física é introduzida (por exemplo, #import ). Isso pode introduzir dependência, mas os mantenedores de bibliotecas geralmente podem manipular as dependencies físicas para você, conforme necessário – se eles introduzirem um recurso, eles poderão minimizar o impacto que ele tem em suas construções.

Eu vejo um monte de “Do it this way”, mas não vejo nenhuma resposta para “Por quê?”

Então: por que você deve @class no seu header e #import apenas na sua implementação? Você está dobrando seu trabalho tendo que @class e #importar o tempo todo. A menos que você faça uso de inheritance. Nesse caso, você estará #importando várias vezes para uma única @class. Então você deve se lembrar de remover vários arquivos diferentes se de repente decidir que não precisa mais acessar uma declaração.

A importação do mesmo arquivo várias vezes não é um problema devido à natureza do #import. A compilation de desempenho também não é um problema. Se fosse, não estaríamos #importando Cocoa / Cocoa.h ou algo assim em praticamente todos os arquivos de header que temos.

se fizermos isso

 @interface Class_B : Class_A 

significa que estamos herdando o Class_A em Class_B, em Class_B podemos acessar todas as variables ​​de class_A.

se estamos fazendo isso

 #import .... @class Class_A @interface Class_B 

aqui estamos dizendo que estamos usando o Class_A em nosso programa, mas se quisermos usar as variables ​​Class_A em Class_B, temos que #importar Class_A no arquivo .m (criar um object e usar sua function e variables).

para informações adicionais sobre dependencies de arquivos & #import & @class, verifique:

http://qualitycoding.org/file-dependencies/ it is good article

resumo do artigo

importações em arquivos de header:

  • #importe a superclass que você está herdando e os protocolos que você está implementando.
  • Forward-declare todo o resto (a menos que venha de um framework com um header master).
  • Tente eliminar todos os outros #imports.
  • Declare protocolos em seus próprios headers para reduzir dependencies.
  • Muitas declarações futuras? Você tem uma class grande.

importações em arquivos de implementação:

  • Elimine os #imports que não são usados.
  • Se um método delega a outro object e retorna o que recebe, tente encaminhar esse object em vez de #importá-lo.
  • Se include um módulo forçar a inclusão de nível após nível de dependencies sucessivas, você poderá ter um conjunto de classs que deseja se tornar uma biblioteca. Construa como uma biblioteca separada com um header principal, para que tudo possa ser incluído como um único bloco pré-construído.
  • Muitos #imports? Você tem uma class grande.

Quando me desenvolvo, tenho apenas três coisas em mente que nunca me causam problemas.

  1. Importar super classs
  2. Importar classs principais (quando você tem filhos e pais)
  3. Importe classs fora do seu projeto (como em frameworks e bibliotecas)

Para todas as outras classs (subclasss e classs filhas no meu próprio projeto), eu as declaro via forward-class.

Se você tentar declarar uma variável, ou uma propriedade em seu arquivo de header, que você ainda não importou, você receberá um erro dizendo que o compilador não conhece esta class.

Seu primeiro pensamento provavelmente é #import isso.
Isso pode causar problemas em alguns casos.

Por exemplo, se você implementar um monte de methods-C no arquivo de header, ou estruturas, ou algo similar, porque eles não devem ser importados várias vezes.

Portanto, você pode dizer ao compilador com @class :

Eu sei que você não conhece essa class, mas existe. Vai ser importado ou implementado em outro lugar

Ele basicamente diz ao compilador para calar a boca e compilar, mesmo que não tenha certeza se esta class será implementada.

Você normalmente usará #import no .m e @class nos arquivos .h .

Encaminhar a declaração apenas para o compilador prevent de mostrar erro.

o compilador saberá que existe uma class com o nome que você usou no seu arquivo de header para declarar.

O compilador só irá reclamar se você for usar essa class de tal maneira que o compilador precise conhecer sua implementação.

Ex:

  1. Isto poderia ser como se você fosse derivar sua class dele ou
  2. Se você vai ter um object dessa class como uma variável de membro (embora rara).

Ele não vai reclamar se você está indo apenas para usá-lo como um ponteiro. Obviamente, você terá que #importá-lo no arquivo de implementação (se estiver instanciando um object dessa class) já que ele precisa conhecer o conteúdo da class para instanciar um object.

NOTA: #import não é o mesmo que #include. Isso significa que não há nada chamado de importação circular. import é uma espécie de requisição para o compilador procurar em um determinado arquivo por alguma informação. Se essa informação já estiver disponível, o compilador a ignorará.

Apenas tente isso, importe Ah em Bh e Bh em Ah Não haverá problemas ou reclamações e funcionará bem também.

Quando usar @class

Você só usa @class se não quiser importar um header no seu header. Este poderia ser um caso em que você nem se importa em saber o que essa class será. Casos em que você talvez nem tenha um header para essa class ainda.

Um exemplo disso pode ser que você está escrevendo duas bibliotecas. Uma class, vamos chamá-la A, existe em uma biblioteca. Esta biblioteca inclui um header da segunda biblioteca. Esse header pode ter um ponteiro de A, mas talvez não precise usá-lo novamente. Se a biblioteca 1 ainda não estiver disponível, a biblioteca B não será bloqueada se você usar @class. Mas se você estiver querendo importar Ah, então o progresso da biblioteca 2 está bloqueado.

Pense em @class como dizendo ao compilador “confie em mim, isso existe”.

Pense em #import como copiar e colar.

Você deseja minimizar o número de importações que você possui por diversos motivos. Sem qualquer pesquisa, a primeira coisa que vem à mente é reduzir o tempo de compilation.

Observe que quando você herda de uma class, você não pode simplesmente usar uma declaração de encaminhamento. Você precisa importar o arquivo, para que a class que você está declarando saiba como é definida.

Este é um cenário de exemplo, onde precisamos de @class.

Considere se você deseja criar um protocolo dentro do arquivo de header, que tem um parâmetro com o tipo de dados da mesma class, então você pode usar @class. Por favor, lembre-se que você também pode declarar protocolos separadamente, isto é apenas um exemplo.

 // DroneSearchField.h #import  @class DroneSearchField; @protocol DroneSearchFieldDelegate @optional - (void)DroneTextFieldButtonClicked:(DroneSearchField *)textField; @end @interface DroneSearchField : UITextField @end