É do meu entendimento que se deve usar uma declaração de classe de encaminhamento caso a Classe A precise incluir um cabeçalho da Classe B e a Classe B precise incluir um cabeçalho da Classe A para evitar inclusões circulares. Também entendo que an #import
é simples, de ifndef
modo que uma inclusão acontece apenas uma vez.
Minha pergunta é a seguinte: quando alguém usa #import
e quando alguém usa @class
? Às vezes, se eu usar uma @class
declaração, 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
declaração direta e colocar um #import
para silenciar os avisos que o compilador está me dando.
fonte
Respostas:
Se você vir este aviso:
você precisa
#import
do arquivo, mas pode fazer isso no seu arquivo de implementação (.m) e usar a@class
declaração no seu arquivo de cabeçalho.@class
(geralmente) não remove a necessidade de#import
arquivos, apenas move o requisito para mais perto de onde as informações são úteis.Por exemplo
Se você diz
@class MyCoolClass
, o compilador sabe que pode ver algo como:Ele não precisa se preocupar com nada além de
MyCoolClass
uma classe válida e deve reservar espaço para um ponteiro para ela (realmente, apenas um ponteiro). Assim, em seu cabeçalho,@class
basta 90% do tempo.No entanto, se você precisar criar ou acessar
myObject
membros, precisará informar ao compilador quais são esses métodos. Neste ponto (presumivelmente no seu arquivo de implementação), você precisará#import "MyCoolClass.h"
informar ao compilador informações adicionais além de apenas "esta é uma classe".fonte
@class
algo em seu.h
arquivo, mas se esqueça de#import
que na .m, tentam acessar um método do@class
objeto ed, e obter avisos como:warning: no -X method found
.Três regras simples:
#import
a superclasse e os protocolos adotados nos arquivos de cabeçalho (.h
arquivos).#import
todas as classes e protocolos para os quais você envia mensagens na implementação (.m
arquivos).Se você encaminhar a declaração nos arquivos de implementação, provavelmente fará algo errado.
fonte
Veja a documentação da linguagem de programação Objective-C no ADC
Sob a seção Definindo uma classe | Interface de classe descreve por que isso é feito:
Eu espero que isso ajude.
fonte
Use uma declaração de encaminhamento no arquivo de cabeçalho, se necessário, e
#import
os arquivos de cabeçalho para todas as classes que você estiver usando na implementação. Em outras palavras, você sempre#import
os arquivos que está usando na sua implementação e, se precisar fazer referência a uma classe no arquivo de cabeçalho, use também uma declaração de encaminhamento.A exceção é que você deve
#import
herdar uma classe ou protocolo formal do arquivo de cabeçalho (nesse caso, não seria necessário importá-lo na implementação).fonte
A prática comum é usar @class nos arquivos de cabeçalho (mas você ainda precisa # importar a superclasse) e # import nos arquivos de implementação. Isso evitará inclusões circulares e simplesmente funcionará.
fonte
#import
"é como a diretiva #include de C, exceto que garante que o mesmo arquivo nunca seja incluído mais de uma vez". Portanto, de acordo com isso,#import
trata-se de inclusões circulares, as@class
diretivas não ajudam particularmente nisso.Outra vantagem: Compilação rápida
Se você incluir um arquivo de cabeçalho, qualquer alteração nele fará com que o arquivo atual também seja compilado, mas esse não será o caso se o nome da classe for incluído como
@class name
. Claro que você precisará incluir o cabeçalho no arquivo de origemfonte
Resposta simples: você
#import
ou#include
quando há uma dependência física. Caso contrário, você pode usar declarações para a frente (@class MONClass
,struct MONStruct
,@protocol MONProtocol
).Aqui estão alguns exemplos comuns de dependência física:
CGPoint
ivar ou propriedade, o compilador precisará ver a declaração deCGPoint
.O compilador é realmente muito indulgente a esse respeito. Ele soltará sugestões (como a acima), mas você pode lixeira sua pilha facilmente se ignorá-las e não fizer isso
#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ências. O que acontece é que o compilador volta ao padrão quando encontra um método desconhecido que você chama. Todo valor e parâmetro de retorno é assumido como sendoid
. Portanto, você deve erradicar todos os avisos de suas bases de código, pois isso deve ser considerado dependência física. Isso é análogo a chamar uma função C que não é declarada. Com C, os parâmetros são assumidos como sendoint
.O motivo de você favorecer as declarações de encaminhamento é que você pode reduzir o tempo de construção por fatores, porque há uma dependência mínima. Com as declarações de encaminhamento, o compilador vê que há um nome e pode analisar e compilar corretamente o programa sem ver a declaração de classe ou todas as suas dependências quando não houver dependência física. Construções limpas levam menos tempo. Construções incrementais levam menos tempo. Claro, você acabará gastando um pouco mais de tempo, garantindo que todos os cabeçalhos necessários sejam visíveis para todas as traduções como conseqüência, mas isso compensa em tempos de compilação reduzidos rapidamente (assumindo que seu projeto não seja pequeno).
Se você usar
#import
ou#include
, em vez disso, estará lançando muito mais trabalho no compilador do que o necessário. Você também está introduzindo dependências complexas de cabeçalho. Você pode comparar isso com um algoritmo de força bruta. Quando você#import
está arrastando toneladas de informações desnecessárias, o que requer muita memória, E / S de disco e CPU para analisar e compilar as fontes.O ObjC é bem próximo do ideal para uma linguagem baseada em C no que diz respeito à dependência, porque os
NSObject
tipos nunca são valores - osNSObject
tipos sempre são indicadores contados como referência. Assim, você poderá obter tempos de compilação incrivelmente rápidos se estruturar adequadamente as dependências do programa e avançar sempre que possível, porque é necessária uma dependência física muito pequena. Você também pode declarar propriedades nas extensões de classe para minimizar ainda mais a dependência. Esse é um grande bônus para sistemas grandes - você saberia a diferença que faz se já desenvolveu uma grande base de código C ++.Portanto, minha recomendação é usar os encaminhadores sempre que possível e depois para
#import
onde houver dependência física. Se você vir o aviso ou outro que implica dependência física - corrija-os todos. A correção está#import
no seu arquivo de implementação.Ao criar bibliotecas, você provavelmente classificará algumas interfaces como um grupo; nesse caso, você classificaria
#import
a biblioteca em que a dependência física é introduzida (por exemplo#import <AppKit/AppKit.h>
). Isso pode introduzir dependência, mas os mantenedores da biblioteca geralmente podem lidar com as dependências físicas conforme necessário - se eles introduzirem um recurso, poderão minimizar o impacto que ele tem nas suas construções.fonte
NSObject types are never values -- NSObject types are always reference counted pointers.
não é inteiramente verdade. Blocos jogam uma brecha na sua resposta, apenas dizendo.Eu vejo muito "Faça dessa maneira", mas não vejo respostas para "Por que?"
Então: por que você deveria @class no cabeçalho e #import apenas na sua implementação? Você está dobrando seu trabalho tendo que @class e #import o tempo todo. A menos que você faça uso da herança. Nesse caso, você estará #importando várias vezes para uma única @class. Lembre-se de remover de vários arquivos diferentes se você decidir de repente que não precisa mais acessar uma declaração.
Importar o mesmo arquivo várias vezes não é um problema devido à natureza de #import. Compilar o desempenho também não é realmente um problema. Se fosse, não estaríamos #importando Cocoa / Cocoa.h ou similares em praticamente todos os arquivos de cabeçalho que temos.
fonte
se fizermos isso
significa que estamos herdando a Class_A na Class_B, na Class_B podemos acessar todas as variáveis da class_A.
se estamos fazendo isso
aqui estamos dizendo que estamos usando o Class_A em nosso programa, mas se quisermos usar as variáveis Class_A no Class_B, precisaremos # importar o Class_A no arquivo .m (crie um objeto e use suas funções e variáveis).
fonte
para obter informações adicionais sobre dependências de arquivos & #import & @class, confira:
http://qualitycoding.org/file-dependencies/ é um bom artigo
resumo do artigo
fonte
Quando me desenvolvo, tenho apenas três coisas em mente que nunca me causam problemas.
Para todas as outras classes (subclasses e classes filho no meu próprio projeto), eu as declaro por meio de classe direta.
fonte
Se você tentar declarar uma variável ou propriedade no seu arquivo de cabeçalho, que ainda não importou, você receberá um erro dizendo que o compilador não conhece essa classe.
Seu primeiro pensamento é provavelmente
#import
isso.Isso pode causar problemas em alguns casos.
Por exemplo, se você implementar vários métodos C no arquivo de cabeçalho, ou estruturas, ou algo semelhante, porque eles não devem ser importados várias vezes.
Portanto, você pode informar ao compilador com
@class
:Basicamente, ele diz ao compilador para desligar e compilar, mesmo que não tenha certeza se essa classe será implementada.
Normalmente você vai usar
#import
na .m e@class
nas .h arquivos.fonte
Encaminhar a declaração apenas para impedir que o compilador mostre erro.
o compilador saberá que há uma classe com o nome que você usou no arquivo de cabeçalho para declarar.
fonte
O compilador reclamará apenas se você for usar essa classe de forma que o compilador precise conhecer sua implementação.
Ex:
Não vai reclamar se você apenas vai usá-lo como um ponteiro. Obviamente, você precisará # importá-lo no arquivo de implementação (se estiver instanciando um objeto dessa classe), pois ele precisa conhecer o conteúdo da classe para instanciar um objeto.
NOTA: #import não é o mesmo que #include. Isso significa que não há nada chamado importação circular. A importação é um tipo de solicitação para o compilador procurar um arquivo específico para obter algumas informações. Se essa informação já estiver disponível, o compilador a ignora.
Apenas tente isso, importe Ah em Bh e Bh em Ah Não haverá problemas ou reclamações e também funcionará bem.
Quando usar @class
Você usa @class apenas se não quiser importar um cabeçalho no seu cabeçalho. Pode ser um caso em que você nem se importa em saber qual será essa aula. Casos em que você ainda não pode ter um cabeçalho para essa classe.
Um exemplo disso pode ser o fato de você estar escrevendo duas bibliotecas. Uma classe, vamos chamá-la de A, existe em uma biblioteca. Esta biblioteca inclui um cabeçalho da segunda biblioteca. Esse cabeçalho pode ter um ponteiro de A, mas novamente pode não ser necessário usá-lo. Se a biblioteca 1 ainda não estiver disponível, a biblioteca B não será bloqueada se você usar @class. Mas se você deseja importar Ah, o progresso da biblioteca 2 é bloqueado.
fonte
Pense no @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 vários motivos. Sem nenhuma pesquisa, a primeira coisa que vem à mente é que reduz o tempo de compilação.
Observe que, quando você herda de uma classe, não pode simplesmente usar uma declaração direta. Você precisa importar o arquivo, para que a classe que você está declarando saiba como é definida.
fonte
Este é um cenário de exemplo, onde precisamos de @class.
Considere se você deseja criar um protocolo no arquivo de cabeçalho, que possui um parâmetro com o tipo de dados da mesma classe, então você pode usar @class. Lembre-se de que você também pode declarar protocolos separadamente, este é apenas um exemplo.
fonte