fuzzygoat
fuzzygoat

Reputation: 26223

ARC Semantic Issue with simple protocol example?

I have just been trying something out with a quick test and I have a question, in the following code:

@protocol stuffieProtocol <NSObject>
@required 
-(void)favouiteBiscuit;
@end

.

// DOG & TED ARE IDENTICAL, THEY JUST LIKE DIFFERENT BISCUITS
@interface Dog : NSObject <stuffieProtocol>
@property (strong, nonatomic) NSString *name;
@end

@implementation Dog

- (id)init {
    return [self initWithName:@"Unknown"];
}

- (id)initWithName:(NSString *)name {
    self = [super init];
    if(self) {
        _name = name;
    }
    return self;
}

- (void)whoAreYou {
    NSLog(@"MY NAME IS: %@ I AM A: %@", [self name], [self class]);
}
- (void)favouiteBiscuit {
    NSLog(@"FAVOURITE BISCUIT IS: Costa Jam Biscuit");
}
@end

.

Dog *stuffie_001 = [[Dog alloc] initWithName:@"Dog Armstrong"];
Ted *stuffie_002 = [[Ted alloc] initWithName:@"Teddy Sullivan"];

NSArray *stuffieArray = @[stuffie_001, stuffie_002];
for(id<stuffieProtocol> eachObject in stuffieArray) {
    [eachObject whoAreYou]; // << ERROR
    [eachObject favouiteBiscuit];
}

My question is I am getting an error "ARC Semantic Issue: No known instance method for selector 'whoAreYou'"

If I prefix [eachObject whoAreYou]; with [(Dog *)eachObject whoAreYou]; then this works for all the iterations of the loop, but that just feels wrong as the all the objects in the array are not of type Dog.

What should I be prefixing this with to be correct?

Upvotes: 3

Views: 2931

Answers (2)

meronix
meronix

Reputation: 6176

well, you declare eachObject as an ID

that's mean that the compiler doesn't know what kind of object it is

it just know that it implements the protocol stuffieProtocol, so surely it can respond to method: favouiteBiscuit

but it doesn't know if it can respond to method whoAreYou

you can do many thing to avoid this

the easiest is:

you could ask if eachObject can perform the selector whoAreYou, and in this case you perform that selector

if ([eachObject respondsToSelector:@selector(whoAreYou) ]) {
    [eachObject performSelector:@selector(whoAreYou) ];
}

this way the compiler avoid to control if eachObject implement the method whoAreYou

it will be done at runtime, so if there a method whoAreYou in eachObject, then ok, it will be called

Another way could be to make a common superclass for both ted and dog

(e.g. SuperClassOfTedAndDog <stuffieProtocol> )

and declare method whoAreYou in that superclass, then in your for loop use that superclass instead of ID:

for(SuperClassOfTedAndDog* eachObject in stuffieArray) {
    [eachObject whoAreYou]; 
    [eachObject favouiteBiscuit];
}

Upvotes: 2

Martin R
Martin R

Reputation: 540005

Add

-(void) whoAreYou;

to your protocol. Then the compiler knows that eachObject in the loop responds to that method.

Upvotes: 2

Related Questions