Вызов метода подкласса для переменной, типизированной как суперкласс - PullRequest
3 голосов
/ 13 декабря 2011

Я создал класс с именем Foo:

@interface Foo:NSObject{
     int myInt;
}
@property int myInt;
@end

и подклассом Foo с именем Bar:

@interface Bar:Foo{
     NSString *myString;
}
@property (copy) NSString *myString;
@end

Я пытаюсь сохранить Bar как Foo объект в массиве, например:

-(void)createBar{
     Foo *object = [[Bar alloc]init];

     // myArray is an instance of NSMutableArray
     [myArray addObject:object];
}

Я делаю это, потому что на самом деле у меня более одного подкласса Foo (я не хочу перечислять их все),Когда я беру объект из массива и отправляю сообщение объекту, чтобы получить переменную myString, приложение ничего не делает. Пример:

-(NSString *)getStringFromFooAtIndex(NSUInteger)index{
     Foo *object = [myArray objectAtIndex:index];

     return [object myString];
}

Не понимаю ли я, как работает «сообщение»?Я предполагал, что могу отправить сообщение объекту, и оно будет вызывать его независимо от того, было оно там или нет.Должен ли я делать это как-то иначе?Массив будет содержать все различные типы Foo дочерних классов, и он мне нужен, чтобы хранить их там.

Ответы [ 3 ]

3 голосов
/ 13 декабря 2011

Я предполагал, что могу отправить сообщение объекту, и оно будет вызывать его независимо от того, было оно там или нет.

Вы действительно можете отправить любое сообщение любому объекту;это часть веселья Objective-C.Тип переменной (Foo *, Bar *, id или любой другой) не влияет на отправку сообщения.Объект, на который указывает переменная, знает свой класс.Поиск соответствующего метода выполняется во время выполнения через этот класс.Компилятор превращает выражение в скобках в вызов функции: objc_msgSend.

При сборке вы должны получить предупреждение о [object myString], говоря: «Foo» может не отвечатьto 'myString' "- компилятор знает, что где-то есть хотя бы один класс, у которого есть метод, соответствующий myString, и он знает, что во время компиляции Foo, похоже, не является одним из них,но это не может гарантировать, что Foo не сможет что-то сделать с сообщением во время выполнения.Сообщения могут быть разрешены пользовательскими способами во время выполнения .Обратите внимание, что если вы измените тип переменной на id, предупреждение исчезнет - компилятор больше не сможет рассуждать о том, какие методы доступны.

Если окажется, что объект, на который вы отправляете myString не не отвечает (т. Е. Если объект действительно a Foo вместо Bar или один из подклассов Foo, который нереализовать myString), будет сгенерировано исключение.Это ответ по умолчанию (от всего, что наследуется от NSObject) на нераспознанное сообщение.

Если у вас есть разнородная коллекция объектов, на которые вам нужно отправлять сообщения, вы, вероятно, захотите проверить каждый объектпервый.Вы можете сделать так, как предложил jstevenco, и проверить работоспособность:

if( [object respondsToSelector:@selector(myString)] ){

или проверить личность:

if( [object isKindOfClass:[Bar class]] ){

Последний пройдет, если объект является Bar или любым изПодклассы Bar.Используйте isMemberOfClass: для проверки только для указанного вами класса.

1 голос
/ 13 декабря 2011

См. здесь для хорошего обзора того, как обрабатывается логика обмена сообщениями. В конечном итоге будет сгенерирована ошибка, если вы не переопределите -(void)doesNotRecognizeSelector:(SEL)aSelector.

Если вы собираетесь создать полиморфную коллекцию объектов, вам, вероятно, следует сначала протестировать объект с помощью respondsToSelector:, а затем либо вызвать метод напрямую, либо использовать один из вариантов performSelector: в соответствии с вашими потребностями. Другая возможность - включить в базовый класс реализацию myString, которая ничего не делает.

0 голосов
/ 14 декабря 2011

Я понял, в чем проблема.

В моем файле AppDelegate.m у меня есть следующий метод:

- (void)createFoo{
     Foo *foo = [[Bar alloc]init];

     [myArrayController add:foo];
}

Это вставка объекта 'foo' в мой контроллер массива. Я также определил этот метод:

- (void) insertObject:(Foo *)object intoMyArrayControllerAtIndex:(NSUInteger)index{
     // handles the inserting of the object as well as working with the undo manager
}

Теперь, вызов [myArrayController add:foo] является причиной проблемы. Если я заменю эту строку на [self insertObect:foo intoMyArrayControllerAtIndex:0], тогда все будет отлично работать.

Большое спасибо и извините за потраченное время.

...