Come lanciare un oggetto in Objective-C


123

Esiste un modo per eseguire il cast di oggetti in Objective-C in modo molto simile al modo in cui gli oggetti vengono lanciati in VB.NET?

Ad esempio, sto cercando di fare quanto segue:

// create the view controller for the selected item
FieldEditViewController *myEditController;
switch (selectedItemTypeID) {
    case 3:
        myEditController = [[SelectionListViewController alloc] init];
        myEditController.list = listOfItems;
        break;
    case 4:
        // set myEditController to a diff view controller
        break;
}

// load the view
[self.navigationController pushViewController:myEditController animated:YES];
[myEditController release]; 

Tuttavia ricevo un errore del compilatore poiché la proprietà "list" esiste nella classe SelectionListViewController ma non su FieldEditViewController anche se SelectionListViewController eredita da FieldEditViewController.

Questo ha senso, ma esiste un modo per eseguire il cast di myEditController su un SelectionListViewController in modo da poter accedere alla proprietà "list"?

Ad esempio in VB.NET farei:

CType(myEditController, SelectionListViewController).list = listOfItems

Grazie per l'aiuto!

Risposte:


216

Ricorda, Objective-C è un superset di C, quindi il typecasting funziona come in C:

myEditController = [[SelectionListViewController alloc] init];
((SelectionListViewController *)myEditController).list = listOfItems;

21
Oppure "Ricorda, Objective-C funziona come Java, ricorda solo di aggiungere asterischi alle variabili che puntano a oggetti Obj-C".
Dan Rosenstark

1
Bella risposta. Potresti renderlo un po 'più chiaro suddividendo il cast e il compito in due righe.
Guido Anselmi

1
Il typecasting in Objective-C è molto più simile al vecchio C che a Java. Java nasconde la maggior parte di questo all'utente, quindi argomenta che il C dovrebbe ancora essere insegnato piuttosto che Java come primo linguaggio.
csmith

11
((SelectionListViewController *)myEditController).list

Altri esempi:

int i = (int)19.5f; // (precision is lost)
id someObject = [NSMutableArray new]; // you don't need to cast id explicitly

7
In generale questo è corretto; non è necessario trasmettere ID nelle espressioni dei messaggi. Ma quando si utilizza la sintassi del punto per accedere e impostare le proprietà, è necessario utilizzare un tipo concreto, non solo id, in modo che il compilatore sappia quale invocazione del metodo generare effettivamente. (Può differire per le proprietà con lo stesso nome.)
Chris Hanson

9

Il typecasting in Objective-C è facile come:

NSArray *threeViews = @[[UIView new], [UIView new], [UIView new]];
UIView *firstView = (UIView *)threeViews[0];

Tuttavia, cosa succede se il primo oggetto non lo è UIViewe provi a usarlo:

NSArray *threeViews = @[[NSNumber new], [UIView new], [UIView new]];
UIView *firstView = (UIView *)threeViews[0];
CGRect firstViewFrame = firstView.frame; // CRASH!

Andrà in crash. Ed è facile trovare un tale arresto anomalo in questo caso, ma cosa succede se quelle righe sono in classi diverse e la terza riga viene eseguita solo una volta su 100 casi. Scommetto che i tuoi clienti trovano questo incidente, non tu! Una soluzione plausibile è bloccarsi presto , in questo modo:

UIView *firstView = (UIView *)threeViews[0];
NSAssert([firstView isKindOfClass:[UIView class]], @"firstView is not UIView");

Queste affermazioni non sembrano molto belle, quindi potremmo migliorarle con questa comoda categoria:

@interface NSObject (TypecastWithAssertion)
+ (instancetype)typecastWithAssertion:(id)object;
@end


@implementation NSObject (TypecastWithAssertion)

+ (instancetype)typecastWithAssertion:(id)object {
    if (object != nil)
        NSAssert([object isKindOfClass:[self class]], @"Object %@ is not kind of class %@", object, NSStringFromClass([self class]));
    return object;
}

@end

Questo è molto meglio:

UIView *firstView = [UIView typecastWithAssertion:[threeViews[0]];

PS Per la sicurezza dei tipi di collezioni, Xcode 7 ha una qualità molto migliore del typecasting - generics


4

Certo, la sintassi è esattamente la stessa di C - NewObj* pNew = (NewObj*)oldObj;

In questa situazione potresti considerare di fornire questo elenco come parametro al costruttore, qualcosa come:

// SelectionListViewController
-(id) initWith:(SomeListClass*)anItemList
{
  self = [super init];

  if ( self ) {
    [self setList: anItemList];
  }

  return self;
}

Quindi usalo in questo modo:

myEditController = [[SelectionListViewController alloc] initWith: listOfItems];

0

Il casting per l'inclusione è importante tanto quanto il casting per l'esclusione per un programmatore C ++. Il casting del tipo non è lo stesso di RTTI, nel senso che puoi eseguire il cast di un oggetto su qualsiasi tipo e il puntatore risultante non sarà nullo.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.