And why should compiler generate an error if we send an id - that conforms to the protocol - the respondsToSelector message
Yes, this is a very odd thing. Nowadays, under ARC, the compiler will throw an error if an object declared as id <XYZPieChartViewDataSource>
is sent the respondsToSelector:
method, the class
method, or any other familiar basic NSObject instance method! Try it and see.
This seems strange, because id
should permit any (known) message to be sent to it. However, id <XYZPieChartViewDataSource>
is considered a completely specific type; the compiler will allow only messages that are part of the XYZPieChartViewDataSource protocol. One modern solution is this: don't use
id <XYZPieChartViewDataSource>
Instead, use
NSObject<XYZPieChartViewDataSource>*
This causes all the yummy goodness of NSObject to be included (as far as the compiler is concerned) in this object, including the ability to respond to respondsToSelector:
and class
and things like that.
Here's a paragraph I've inserted in my book, discussing this very issue:
Curiously, the compiler treats an object typed as
id<SomeProtocol>
very differently from how it treats an object typed asid
, allowing only methods defined in SomeProtocol to be sent to that object. For example, suppose MyClass is defined with adelegate
property typed asid<MyProtocol>
. Then ifobj
is a MyClass instance, you can't speak of[obj.delegate class]
; the compiler complains thatclass
is not a known instance method! That's becauseobj.delegate
is typed asid<MyProtocol>
, and thus its only known instance method isdoSomething:
, the method defined by MyProtocol. We can work around this by castingobj.delegate
to anid
; a more elegant solution, when the definition of MyClass is up to us, is to declare thedelegate
property asNSObject<MyProtocol>*
instead ofid<MyProtocol>
.