Как привести объект в Objective-C

124

Есть ли способ приведения объектов в объекте-c так же, как при приведении объектов в VB.NET?

Например, я пытаюсь сделать следующее:

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

Однако я получаю ошибку компилятора, поскольку свойство «список» существует в классе SelectionListViewController, но не в FieldEditViewController, хотя SelectionListViewController наследуется от FieldEditViewController.

В этом есть смысл, но есть ли способ привести myEditController к SelectionListViewController, чтобы я мог получить доступ к свойству list?

Например, в VB.NET я бы сделал:

CType(myEditController, SelectionListViewController).list = listOfItems

Спасибо за помощь!

Билли
источник

Ответы:

218

Помните, что Objective-C - это надмножество C, поэтому приведение типов работает так же, как и в C:

myEditController = [[SelectionListViewController alloc] init];
((SelectionListViewController *)myEditController).list = listOfItems;
Джим Пульс
источник
21
Или «Помните, Objective-C работает как Java, только не забудьте добавить звездочки к переменным, указывающим на объекты Obj-C».
Дэн Розенстарк,
1
Отличный ответ. Вы можете сделать это немного понятнее, разбив приведение и назначение на две строки.
Guido Anselmi
1
Приведение типов в Objective-C больше похоже на старый C, чем на Java. Java скрывает большую часть этого от пользователя, отсюда и аргументы в пользу того, что следует изучать C, а не Java в качестве первого языка.
csmith 02
11
((SelectionListViewController *)myEditController).list

Еще примеры:

int i = (int)19.5f; // (precision is lost)
id someObject = [NSMutableArray new]; // you don't need to cast id explicitly
Сиджмен Малдер
источник
7
В целом это правильно; вам не нужно указывать id в выражениях сообщений. Но при использовании точечного синтаксиса для доступа и установки свойств вы должны использовать конкретный тип, а не только id, чтобы компилятор знал, какой вызов метода фактически генерировать. (Он может отличаться для свойств с тем же именем.)
Крис Хэнсон,
9

Приведение типов в Objective-C очень просто:

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

Однако что произойдет, если первого объекта нет UIViewи вы попытаетесь его использовать:

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

Он рухнет. И найти такой сбой для этого случая легко, но что, если эти строки находятся в разных классах, а третья строка выполняется только один раз из 100 случаев. Бьюсь об заклад, это сбой обнаруживают ваши клиенты, а не вы! Правдоподобным решением является ранний сбой , например:

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

Эти утверждения выглядят не очень красиво, поэтому мы могли бы улучшить их с помощью этой удобной категории:

@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

Это намного лучше:

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

PS Для безопасности типов коллекций у Xcode 7 есть намного лучше, чем приведение типов - generics

Александр Васенин
источник
4

Конечно, синтаксис точно такой же, как у C - NewObj* pNew = (NewObj*)oldObj;

В этой ситуации вы можете рассмотреть возможность предоставления этого списка в качестве параметра конструктору, например:

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

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

  return self;
}

Тогда используйте это так:

myEditController = [[SelectionListViewController alloc] initWith: listOfItems];
Эндрю Грант
источник
0

Приведение для включения так же важно, как и приведение для исключения для программиста на C ++. Приведение типов отличается от RTTI в том смысле, что вы можете привести объект к любому типу, и результирующий указатель не будет нулевым.

Стивен
источник