Jak rzucić obiekt w Objective-C

124

Czy istnieje sposób na rzucanie obiektów w Objective-C, podobnie jak w przypadku rzutowania obiektów w VB.NET?

Na przykład próbuję wykonać następujące czynności:

// 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]; 

Jednak otrzymuję błąd kompilatora, ponieważ właściwość „list” istnieje w klasie SelectionListViewController, ale nie w FieldEditViewController, mimo że SelectionListViewController dziedziczy z FieldEditViewController.

Ma to sens, ale czy istnieje sposób na rzutowanie myEditController na SelectionListViewController, aby uzyskać dostęp do właściwości „list”?

Na przykład w VB.NET zrobiłbym:

CType(myEditController, SelectionListViewController).list = listOfItems

Dzięki za pomoc!

Pałka policjanta
źródło

Odpowiedzi:

218

Pamiętaj, że Objective-C jest nadzbiorem języka C, więc rzutowanie typów działa tak, jak w C:

myEditController = [[SelectionListViewController alloc] init];
((SelectionListViewController *)myEditController).list = listOfItems;
Jim Puls
źródło
21
Lub „Pamiętaj, Objective-C działa jak Java, pamiętaj tylko o dodawaniu gwiazdek do zmiennych wskazujących na obiekty Obj-C”.
Dan Rosenstark
1
Świetna odpowiedź. Możesz to trochę wyjaśnić, dzieląc obsadę i przypisanie na dwie linie.
Guido Anselmi
1
Rzutowanie w Objective-C jest dużo bardziej podobne do starego C niż Javy. Java ukrywa większość tego przed użytkownikiem, stąd argumenty, że nadal należy uczyć języka C, a nie Javy jako pierwszego języka.
csmith
11
((SelectionListViewController *)myEditController).list

Więcej przykładów:

int i = (int)19.5f; // (precision is lost)
id someObject = [NSMutableArray new]; // you don't need to cast id explicitly
Sijmen Mulder
źródło
7
Ogólnie jest to poprawne; nie musisz rzucać id w wyrażeniach wiadomości. Ale gdy używasz składni kropkowej do uzyskiwania dostępu i ustawiania właściwości, musisz użyć konkretnego typu, a nie tylko identyfikatora, aby kompilator wiedział, jakie wywołanie metody ma faktycznie wygenerować. (Może się różnić w przypadku nieruchomości o tej samej nazwie.)
Chris Hanson,
9

Typowanie w Objective-C jest łatwe, ponieważ:

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

Co się jednak stanie, jeśli pierwszy obiekt nie jest UIViewi spróbujesz go użyć:

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

To się rozbije. I łatwo jest znaleźć taką awarię dla tego przypadku, ale co, jeśli te linie należą do różnych klas, a trzecia linia jest wykonywana tylko raz na 100 przypadków. Założę się, że to Twoi klienci to katastrofa, a nie Ty! Prawdopodobnym rozwiązaniem jest wczesna awaria , na przykład:

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

Te stwierdzenia nie wyglądają zbyt ładnie, więc moglibyśmy je ulepszyć dzięki tej przydatnej kategorii:

@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

To jest o wiele lepsze:

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

PS W przypadku kolekcji bezpieczeństwa typu Xcode 7 ma znacznie lepsze wyniki niż rzutowanie typów - generics

Alexander Vasenin
źródło
4

Jasne, składnia jest dokładnie taka sama jak C - NewObj* pNew = (NewObj*)oldObj;

W tej sytuacji możesz rozważyć dostarczenie tej listy jako parametru do konstruktora, na przykład:

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

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

  return self;
}

Następnie użyj tego w ten sposób:

myEditController = [[SelectionListViewController alloc] initWith: listOfItems];
Andrew Grant
źródło
0

Rzutowanie na włączenie jest tak samo ważne jak rzutowanie na wykluczenie dla programisty C ++. Rzutowanie typów nie jest tym samym, co w przypadku RTTI w tym sensie, że można rzutować obiekt na dowolny typ, a wynikowy wskaźnik nie będzie zerowy.

Stephen
źródło