2015-06-07 2 views
3

я не получаю ошибки из этого кода:Почему вы можете назначить указатель одного типа указателю другого типа во встраиваемом цикле?

NSArray* animals = [NSArray arrayWithObjects: 
        [[Dog alloc] init], 
        [[Cat alloc] init], 
        nil]; 

for (Dog* dog in animals) { 
    if ([dog respondsToSelector:@selector(bark)]) { 
     [dog bark]; 
    } 

} 

Вот собаки и кошки классы:

// 
// Dog.h 
// Test1 

#import <Foundation/Foundation.h> 

@interface Dog : NSObject 

- (void) bark; 

@end 

...

// 
// Cat.h 
// Test1 
// 

#import <Foundation/Foundation.h> 

@interface Cat : NSObject 

@end 

...

// 
// Cat.m 
// Test1 
// 

#import "Cat.h" 

@implementation Cat 

@end 

И, я только получаю предупреждение, если я это сделать:

Dog* myDog = [[Cat alloc] init]; 
+0

Итератор массива возвращает объекты типа 'id', которые могут быть назначены на все (' Dog') в вашем случае. – rmaddy

+0

@rmaddy, ладно, спасибо. Далее, почему я могу реализовать метод bark() в классе Cat, а затем сделать это: 'Dog * myDog = [[Cat alloc] init]; [myDog bark]; 'По той же причине? init() в NSObject возвращает тип id? Да: '- (instancetype) init'. Как отправить комментарий в качестве ответа? – 7stud

+0

Назначение объекта 'Cat' переменной' Dog' не должно компилироваться (или, по крайней мере, давать предупреждение, которое не следует игнорировать). Но, несмотря на то, что 'myDog' объявлен как переменная' Dog', он фактически указывает на объект 'Cat' во время выполнения, поэтому вы можете вызывать любой метод« Cat »без проблем. – rmaddy

ответ

2

Похоже, вы получили большинство свои вопросы в комментариях, но я думал, что писать ответ на, чтобы заполнить любые недостающие пробелы.

NSArray принимает идентификатор, и вы можете пройти в любом подклассе NSObject, который вы хотите, и он позволит ему выполнить его. Это НЕ РЕКОМЕНДУЕТСЯ, но может быть сделано.

NSArray* animals = [NSArray arrayWithObjects: 
        [[Dog alloc] init], 
        [[Cat alloc] init], 
        @"This is a string", 
        @(42), 
        nil]; 

Даже кулер - это то, что когда вы вытаскиваете предметы из массива, они выходят как id. Это становится отличным от любых переменных, которые вы используете.

Dog *dog = animals[0]; 
Cat *cat = animals[1]; 

Компилятор не будет волноваться, если вы сделаете это.

NSString *aString = animals[0]; 
NSLog(@"%@", aString); 
NSLog(@"%@", [aString lowercaseString]); 

Это то, что вы получите в журнале ...

2015-06-07 08:55:53.715 DogsAndCats2[5717:4029814] <Dog: 0x7fe790c71b80> 
2015-06-07 08:55:53.715 DogsAndCats2[5717:4029814] -[Dog lowercaseString]: unrecognized selector sent to instance 0x7fe790c71b80 

Вы бросаете идентификатор к NSString, который позволил, однако, вы пытаетесь вызвать метод, который не существует на Собака и рухнет на второй NSLog. Обратите внимание, что первый журнал говорит, что он по-прежнему является Собакой, хотя он заносится как NSString.

Теперь, если мы добавим свойство к вашему Dog.h, мы можем увидеть некоторые опрятные вещи в цикле for.

Dog.h

#import <UIKit/UIKit.h> 

@interface Dog : NSObject 

@property (nonatomic, strong)NSString *dogName; 

-(void)bark; 

@end 

Теперь, когда мы цикл рассмотрим следующее.

for (id thing in animals) 
{ 
    if ([thing respondsToSelector:@selector(bark)]) 
    { 
     //You are allowed to send messages to id 
     [thing bark]; 
     [thing setDogName:@"Lassie"]; 

     //compiler error because it doesn't know it is a Dog class 
     //thing.dogName = @"Lassie"; 

    } 

    if ([thing isKindOfClass:[Dog class]]) 
    { 
     [thing bark]; 

     //this is safe because we checked the class first and now we can cast it as so 
     Dog *dog = (Dog *)thing; 
     dog.dogName = @"Lassie"; 
    } 

} 

Надеюсь, это ответит на ваши вопросы и не слишком смущает.

+0

Спасибо за ответ. 'Вещь.dogName = @ "Lassie"; // ошибка компилятора, потому что она не знает, что это класс Dog'. Хм ... Я думал, что это семантический сахар для '[вещь setDogName: @" Lassie "]'. – 7stud

+0

Они действительно означают одно и то же, но это именно то, что компилятор будет и не допустит. Вы можете использовать метод непосредственно по id, если тот, который импортируется, имеет этот метод. '[вещь addSubview: self.view]; 'Компилятор разрешит, но сильно сработает. 'addSubView:' поступает из UIKit, и компилятор знает, что некоторые объекты могут использовать это, а id - «некоторый объект» для компилятора. –

+0

@ 7stud Вы также можете найти эту ссылку полезной. http://stackoverflow.com/a/1208421/851041 –

Смежные вопросы