Why can you assign a pointer of one type to a pointer of another type in a for-in loop?

I am not getting errors from this code:

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

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

}

      

Here are the classes of dogs and cats:

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

      

And I only get a warning if I do this:

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

      

+3


source to share


1 answer


It looks like you got your questions answered in the comments, but I thought I'd write an answer to fill in any missing gaps.

NSArray takes an ID and you can pass into any NSObject subclass you want and this will allow it to execute. This is NOT RECOMMENDED but can be done.

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

      

Even the cooler is that when you pull elements out of the array, they come out as id. This gets different from whatever variables you use.

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

      

The compiler won't worry if you do this.

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

      

This is what you get in the magazine ...



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

      

You are sending an id to the NSString which is allowed, however you are trying to call a method that does not exist on Dog and the second NSLog fails. Note that the first log says it is still a Dog, although it is distinct as an NSString.

Now if we add a property to your Dog.h, we can see some neat things in the for loop.

Dog.h

#import <UIKit/UIKit.h>

@interface Dog : NSObject

@property (nonatomic, strong)NSString *dogName;

-(void)bark;

@end

      

Now that we are looping, consider the following.

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";
    }

}

      

Hopefully this answers your questions more and doesn't get too confusing.

+2


source







All Articles