Respostas:
void *
significa "uma referência a algum pedaço aleatório da memória com conteúdo não digitado / desconhecido"
id
significa "uma referência a algum objeto Objective-C aleatório de classe desconhecida"
Existem diferenças semânticas adicionais:
Nos modos GC Only ou GC Supported, o compilador emitirá barreiras de gravação para referências do tipo id
, mas não para o tipo void *
. Ao declarar estruturas, isso pode ser uma diferença crítica. Declarar iVars como void *_superPrivateDoNotTouch;
causará a colheita prematura de objetos, se _superPrivateDoNotTouch
for realmente um objeto. Não faça isso.
tentar invocar um método em uma referência do void *
tipo exibirá um aviso do compilador.
tentar invocar um método em um id
tipo só avisará se o método que está sendo chamado não tiver sido declarado em nenhuma das @interface
declarações vistas pelo compilador.
Portanto, nunca se deve se referir a um objeto como a void *
. Da mesma forma, deve-se evitar o uso de uma id
variável digitada para se referir a um objeto. Use a referência digitada da classe mais específica possível. Even NSObject *
é melhor do que id
porque o compilador pode, pelo menos, fornecer uma melhor validação de invocações de método contra essa referência.
O uso comum e válido de void *
é como uma referência de dados opaca que é passada por outra API.
Considere o sortedArrayUsingFunction: context:
método de NSArray
:
- (NSArray *)sortedArrayUsingFunction:(NSInteger (*)(id, id, void *))comparator context:(void *)context;
A função de classificação seria declarada como:
NSInteger mySortFunc(id left, id right, void *context) { ...; }
Nesse caso, o NSArray simplesmente passa o que você passa como context
argumento para o método e como context
argumento. É um pedaço opaco de dados do tamanho de ponteiros, no que diz respeito ao NSArray, e você é livre para usá-los para qualquer finalidade que desejar.
Sem um recurso de tipo de fechamento no idioma, essa é a única maneira de transportar um monte de dados com uma função. Exemplo; se você quiser que mySortFunc () classifique condicionalmente como distinção entre maiúsculas e minúsculas ou maiúsculas e minúsculas, embora ainda seja seguro para threads, você passaria o indicador de maiúsculas e minúsculas no contexto, provavelmente transmitindo a entrada e saída.
Frágil e propenso a erros, mas o único caminho.
Os blocos resolvem isso - os blocos são fechos para C. Eles estão disponíveis em Clang - http://llvm.org/ e são difundidos no Snow Leopard ( http://developer.apple.com/library/ios/documentation/Performance /Reference/GCD_libdispatch_Ref/GCD_libdispatch_Ref.pdf ).
id
responde. Um id
pode facilmente se referir a uma instância de uma classe que não é inerente a NSObject
. Na prática, porém, sua afirmação combina melhor com o comportamento do mundo real; você não pode misturar <NSObject>
classes não implementadas com a API Foundation e ir muito longe, isso é definitivamente certo!
id
e Class
tipos estão a ser tratados como ponteiro objecto retainable sob ARC. Portanto, a suposição é verdadeira pelo menos no ARC.
id é um ponteiro para um objeto C objetivo, onde void * é um ponteiro para qualquer coisa.
id também desativa os avisos relacionados à chamada de métodos desconhecidos, por exemplo:
[(id)obj doSomethingWeirdYouveNeverHeardOf];
não emitirá o aviso usual sobre métodos desconhecidos. Obviamente, isso gerará uma exceção no tempo de execução, a menos que obj seja nulo ou realmente implemente esse método.
Freqüentemente, você deve usar NSObject*
ou id<NSObject>
preferir id
, o que pelo menos confirma que o objeto retornado é um objeto Cocoa, para que você possa usar com segurança métodos como reter / liberar / liberar automaticamente.
Often you should use NSObject*
vez de id
. Ao especificar, NSObject*
você está realmente dizendo explicitamente que o objeto é um NSObject. Qualquer chamada de método para o objeto resultará em um aviso, mas nenhuma exceção de tempo de execução enquanto esse objeto realmente responder à chamada de método. O aviso é obviamente irritante, então id
é melhor. De grosso modo, você pode ser mais específico, por exemplo id<MKAnnotation>
, dizendo , que neste caso significa qualquer que seja o objeto, ele deve estar em conformidade com o protocolo MKAnnotation.
Se um método tiver um tipo de id
retorno, você poderá retornar qualquer objeto Objective-C.
void
significa que o método não retornará nada.
void *
é apenas um ponteiro. Você não poderá editar o conteúdo no endereço apontado pelo ponteiro.
id
é um ponteiro para um objeto Objective-C. void *
é um ponteiro para qualquer coisa . Você pode usar em void *
vez de id
, mas não é recomendado, porque você nunca receberá avisos do compilador para nada.
Você pode querer ver stackoverflow.com/questions/466777/whats-the-difference-between-declaring-a-variable-id-and-nsobject e unixjunkie.blogspot.com/2008/03/id-vs-nsobject-vs -id.html .
void *
variáveis digitadas definitivamente podem ser o alvo de invocações de métodos - é um aviso, não um erro. Não só isso, você pode fazer isso: int i = (int)@"Hello, string!";
e acompanhar com: printf("Sending to an int: '%s'\n", [i UTF8String]);
. É um aviso, não um erro (e não é exatamente recomendado, nem portátil). Mas a razão pela qual você pode fazer essas coisas é tudo C. básica
/// Represents an instance of a class.
struct objc_object {
Class isa OBJC_ISA_AVAILABILITY;
};
/// A pointer to an instance of a class.
typedef struct objc_object *id;
O código acima é de objc.h, então parece que id é uma instância de objc_object struct e um ponteiro pode ser vinculado a qualquer objeto Objective C Class, enquanto void * é apenas um ponteiro sem tipo.
Meu entendimento é que id representa um ponteiro para um objeto, enquanto void * pode apontar para algo realmente, desde que você o transmita para o tipo que deseja usá-lo como
Além do que já foi dito, há uma diferença entre objetos e ponteiros relacionados a coleções. Por exemplo, se você quiser inserir algo no NSArray, precisará de um objeto (do tipo "id") e não poderá usar um ponteiro de dados brutos (do tipo "void *"). Você pode usar [NSValue valueWithPointer:rawData]
para converter void *rawDdata
para o tipo "id" para usá-lo dentro de uma coleção. Em geral, "id" é mais flexível e tem mais semântica relacionada a objetos anexados a ele. Há mais exemplos explicando o tipo de identificação do objetivo C aqui .
id
suposta resposta é-retain
e-release
, enquanto avoid*
é completamente opaca ao chamado. Você não pode passar um ponteiro arbitrário para-performSelector:withObject:afterDelay:
(ele retém o objeto) e não pode presumir que+[UIView beginAnimations:context:]
reterá o contexto (o representante da animação deve manter a propriedade do contexto; o UIKit mantém o representante da animação).