Existem coleções fortemente tipadas no Objective-C?

140

Eu sou novo na programação Mac / iPhone e Objective-C. Em C # e Java, temos "genéricos", classes de coleção cujos membros só podem ser do tipo declarado. Por exemplo, em c #

Dictionary<int, MyCustomObject>

pode conter apenas chaves inteiros e valores do tipo MyCustomObject. Existe um mecanismo semelhante no Objective-C?

Rico
fonte
Estou começando a aprender sobre a ObjC. Talvez você possa usar o ObjC ++ para fazer o trabalho pesado?
Toybuilder
Você pode estar interessado nas respostas para esta pergunta: Existe alguma maneira de aplicar a digitação no NSArray, NSMutableArray etc.? . Argumentos são apresentados por que não é prática comum no Objective-C / Cocoa.
Mouviciel 11/05/09
2
O ObjC ++ não é realmente uma linguagem ... apenas uma maneira de referenciar a capacidade do ObjC de lidar com o C ++ inline da mesma forma que trataria com C. Você não deve fazer isso a menos que precise (embora seja necessário). para usar uma biblioteca de terceiros gravada em C ++).
1111 Marc W
Praticamente uma duplicata exata de stackoverflow.com/questions/649483/…
Barry Wark
@ Mark W - "não deveria fazer isso" por que não? Eu usei o ObjC ++ e funciona muito bem. Eu posso fazer #import <map> e @property std :: map <int, NSString *> myDict; Eu posso usar a API do cacau completa E ter coleções fortemente tipadas. Não vejo nenhum lado negativo.
precisa

Respostas:

211

No Xcode 7, a Apple introduziu 'Lightweight Generics' no Objective-C. No Objective-C, eles geram avisos do compilador se houver uma incompatibilidade de tipo.

NSArray<NSString*>* arr = @[@"str"];

NSString* string = [arr objectAtIndex:0];
NSNumber* number = [arr objectAtIndex:0]; // Warning: Incompatible pointer types initializing 'NSNumber *' with an expression of type 'NSString *'

E no código Swift, eles produzirão um erro do compilador:

var str: String = arr[0]
var num: Int = arr[0] //Error 'String' is not convertible to 'Int'

Os Lightweight Generics devem ser usados ​​com NSArray, NSDictionary e NSSet, mas você também pode adicioná-los às suas próprias classes:

@interface GenericsTest<__covariant T> : NSObject

-(void)genericMethod:(T)object;

@end

@implementation GenericsTest

-(void)genericMethod:(id)object {}

@end

O Objective-C se comportará como antes com os avisos do compilador.

GenericsTest<NSString*>* test = [GenericsTest new];

[test genericMethod:@"string"];
[test genericMethod:@1]; // Warning: Incompatible pointer types sending 'NSNumber *' to parameter of type 'NSString *'

mas Swift ignorará completamente as informações genéricas. (Não é mais verdade no Swift 3+.)

var test = GenericsTest<String>() //Error: Cannot specialize non-generic type 'GenericsTest'

Além dessas classes de coleção Foundation, os genéricos leves Objective-C são ignorados pelo Swift. Quaisquer outros tipos que usam genéricos leves são importados para o Swift como se não fossem parametrizados.

Interagindo com APIs do Objective-C

Connor
fonte
Como tenho perguntas sobre genéricos e tipos retornados nos métodos, fiz minha pergunta em diferentes threads, para manter tudo claro: stackoverflow.com/questions/30828076/…
lvp:
2
@rizzes. Sim, foi apenas introduzido.
Connor
Uma ressalva aqui é que o Swift não ignora completamente as anotações de tipo na sua classe ObjC genérica. Se você especificar restrições, por exemplo MyClass <Foo: id<Bar>>, seu código Swift assumirá que os valores são do tipo de sua restrição, o que lhe dá algo para trabalhar. No entanto, subclasses especializadas MyClassteriam seus tipos especializados ignorados (ser vistos efetivamente da mesma forma que um genérico MyClass). Veja github.com/bgerstle/LightweightGenericsExample
Brian Gerstle
Então, isso é compilado para os sistemas operacionais 10.10, 10.9 e anteriores?
P0lAris
Desde que você defina seu destino de implantação para suportá-los
Connor
91

Esta resposta está desatualizada, mas permanece com valor histórico. A partir do Xcode 7, a resposta de Connor de 8 de junho de 15 é mais precisa.


Não, não há genéricos no Objective-C, a menos que você queira usar modelos C ++ em suas próprias classes de coleção personalizadas (o que eu desencorajo fortemente).

O Objective-C possui digitação dinâmica como um recurso, o que significa que o tempo de execução não se importa com o tipo de um objeto, pois todos os objetos podem receber mensagens. Quando você adiciona um objeto a uma coleção interna, eles são tratados apenas como se fossem do tipo id. Mas não se preocupe, basta enviar mensagens para esses objetos como normal; funcionará bem (a não ser, é claro, que um ou mais objetos da coleção não respondam à mensagem que você está enviando) .

Os genéricos são necessários em linguagens como Java e C # porque são linguagens fortes e de tipo estatístico. Jogo totalmente diferente do recurso de digitação dinâmica do Objective-C.

Marc W
fonte
88
Discordo de "não se preocupe, basta enviar mensagens para esses objetos". Se você colocar o tipo errado de objetos na coleção, que não responde a essas mensagens, isso gerará erros de tempo de execução. O uso de genéricos em outros idiomas evita esse problema com verificações de tempo de compilação.
precisa saber é o seguinte
8
@ henning77 Sim, mas o Objective-C é uma linguagem mais dinâmica do que essas linguagens. Se você deseja uma segurança de tipo forte, use esses idiomas.
Raffi Khatchadourian
36
Também discordo ao não filosofia preocupação - por exemplo, se você puxar o primeiro item para fora de um NSArray e lançá-lo para um NSNumber mas esse item foi realmente um NSString, você está ferrado ...
jjxtra
13
@RaffiKhatchadourian - não há muita escolha se você estiver escrevendo um aplicativo para iOS. Se fosse simples escrever um com Java e obter todos os benefícios de escrever um aplicativo nativo, acredite: eu faria.
ericsoco
11
A maior reclamação que tenho sobre isso não está relacionada a linguagens dinâmicas versus verificação do tempo de compilação, mas à simples comunicação do desenvolvedor. Não posso apenas olhar para uma declaração de propriedade e saber que tipo de objetos ela retornará, a menos que esteja documentada em algum lugar.
devios1
11

Não, mas, para deixar mais claro, você pode comentar com o tipo de objeto que deseja armazenar, já vi isso feito algumas vezes quando você precisa escrever algo no Java 1.4 atualmente), por exemplo:

NSMutableArray* /*<TypeA>*/ arrayName = ....

ou

NSDictionary* /*<TypeA, TypeB>*/ dictionaryName = ...
Mark Rhodes
fonte
Eu acho que essa é uma boa maneira de documentá-lo, caso outra pessoa leia seu código. De qualquer forma, o nome da variável deve ser o mais claro possível para saber quais objetos ela contém.
Htafoya
6

Não há genéricos no Objective-C.

Dos documentos

Matrizes são coleções ordenadas de objetos. O Cocoa fornece várias classes de matriz, NSArray, NSMutableArray (uma subclasse de NSArray) e NSPointerArray.

Matthew Vines
fonte
O link para o documento na resposta está esgotado - "Desculpe, mas essa página não foi encontrada" .
21418 Pang
5

Isso foi lançado no Xcode 7 (finalmente!)

Observe que no código do Objective C, é apenas uma verificação em tempo de compilação; não haverá erro em tempo de execução apenas para colocar o tipo errado em uma coleção ou atribuir a uma propriedade digitada.

Declarar:

@interface FooClass <T> : NSObject
@property (nonatomic) T prop;
@end

Usar:

FooClass<NSString *> *foo = [[FooClass alloc] init];
NSArray<FooClass<NSString *> *> *fooAry = [NSArray array];

Tenha cuidado com esses *s.

Kevin
fonte
4

Os NSArrays genéricos podem ser realizados subclassificando NSArraye redefinindo todos os métodos fornecidos com métodos mais restritivos. Por exemplo,

- (id)objectAtIndex:(NSUInteger)index

teria que ser redefinido em

@interface NSStringArray : NSArray

Como

- (NSString *)objectAtIndex:(NSUInteger)index

para que um NSArray contenha apenas NSStrings.

A subclasse criada pode ser usada como uma substituição imediata e traz muitos recursos úteis: avisos do compilador, acesso a propriedades, melhor criação de código e conclusão no Xcode. Todos esses são recursos em tempo de compilação, não há necessidade de redefinir a implementação real - os métodos do NSArray ainda podem ser usados.

É possível automatizar isso e resumir em apenas duas instruções, o que o aproxima de idiomas que suportam genéricos. Eu criei uma automação com o WMGenericCollection , onde os modelos são fornecidos como macros de pré-processador C.

Após importar o arquivo de cabeçalho que contém a macro, você pode criar um NSArray genérico com duas instruções: uma para a interface e outra para a implementação. Você só precisa fornecer o tipo de dados que deseja armazenar e nomes para suas subclasses. WMGenericCollection fornece tais modelos para NSArray, NSDictionarye NSSet, assim como os seus homólogos mutáveis.

Um exemplo: List<int>pode ser realizado por uma classe personalizada chamada NumberArray, criada com a seguinte instrução:

WMGENERICARRAY_INTERFACE(NSNumber *, // type of the value class
                         // generated class names
                         NumberArray, MutableNumberArray)

Depois de criar NumberArray, você pode usá-lo em qualquer lugar do seu projeto. Falta a sintaxe de <int>, mas você pode escolher seu próprio esquema de nomenclatura para rotulá-los como classes como modelos.

ui
fonte
observe que o mesmo existe no CoreLib: github.com/core-code/CoreLib/blob/master/CoreLib/CoreLib.h#L105
user1259710
2

Agora os sonhos se tornam realidade - existem genéricos no Objective-C desde hoje (obrigado, WWDC). Não é uma piada - na página oficial do Swift:

Novos recursos de sintaxe permitem que você escreva códigos mais expressivos, melhorando a consistência no idioma. Os SDKs empregaram novos recursos do Objective-C, como anotação de genéricos e de nulidade, para tornar o código Swift ainda mais limpo e seguro. Aqui está apenas uma amostra dos aprimoramentos do Swift 2.0.

E imagem que prova isso:Genéricos Objective-C

htzfun
fonte
2

Só quero pular aqui. Eu escrevi um post aqui sobre Generics.

O que eu quero contribuir é que os genéricos possam ser adicionados a qualquer classe , não apenas às classes de coleção, como a Apple indica.

Adicionei com sucesso a uma variedade de classes, pois elas funcionam exatamente da mesma maneira que as coleções da Apple. ie verificação do tempo de compilação, conclusão do código, permitindo a remoção de transmissões, etc.

Aproveitar.

drekka
fonte
-2

As classes Collections fornecidas pelas estruturas Apple e GNUStep são semi-genéricas, pois assumem que recebem objetos, algumas que podem ser classificadas e outras que respondem a determinadas mensagens. Para primitivas, como floats, ints, etc, toda a estrutura de matrizes C está intacta e pode ser usada, e há objetos wrapper especiais para eles para uso nas classes de coleção geral (por exemplo, NSNumber). Além disso, uma classe Collection pode ser subclassificada (ou modificada especificamente por categorias) para aceitar objetos de qualquer tipo, mas você deve escrever todo o código de manipulação de tipos. As mensagens podem ser enviadas para qualquer objeto, mas devem retornar nulo se for inapropriado para o objeto ou a mensagem deve ser encaminhada para um objeto apropriado. Erros de tipo verdadeiro devem ser capturados no tempo de compilação, não no tempo de execução. No tempo de execução, eles devem ser manipulados ou ignorados. Por fim, o Objc fornece recursos de reflexão em tempo de execução para lidar com casos complicados e a resposta da mensagem, tipo específico e serviços podem ser verificados em um objeto antes que ele seja enviado ou colocado em uma coleção inadequada. Cuidado que bibliotecas e estruturas díspares adotam convenções diferentes sobre como seus objetos se comportam quando são enviadas mensagens para as quais eles não têm respostas de código, portanto, RTFM. Além de programas de brinquedo e compilações de depuração, a maioria dos programas não deve travar, a menos que realmente estrague tudo e tente gravar dados ruins na memória ou no disco, executar operações ilegais (por exemplo, dividir por zero, mas você também pode capturar isso) ou acessar recursos do sistema fora dos limites. O dinamismo e o tempo de execução do Objective-C permitem que as coisas falhem normalmente e devem ser incorporados ao seu código. (DICA) se você está tendo problemas com a genéricos em suas funções, tente alguma especificidade. Escreva as funções com tipos específicos e deixe o tempo de execução selecionar (é por isso que são chamados de seletores!) A função-membro apropriada no tempo de execução.

Example:
    -(id) sort (id) obj;  // too generic. catches all.
     // better
    -(id) sort: (EasilySortableCollection*) esc;
    -(id) sort: (HardToSortCollection*) hsc; 
    ...
    [Sorter  sort: MyEasyColl];
    [Sorter  sort: MyHardColl];
Chris Reid
fonte