I came across this code in ShareKit, and I don't understand what the writer had in mind, using self
in a class method. There is a warning: Incompatible pointer types sending 'Class' to parameter type id<FBSessionDelegate>
. I want to clean up these warnings, so I can see the ones that may hurt later. What can/should I do that won't break this?
This is is the file SHKFacebook.m and the class name is SHKFacebook
+ (void)logout
{
FBSession *fbSession;
if(!SHKFacebookUseSessionProxy){
fbSession = [FBSession sessionForApplication:SHKFacebookKey
secret:SHKFacebookSecret
delegate:self];
}else {
fbSession = [FBSession sessionForApplication:SHKFacebookKey
getSessionProxy:SHKFacebookSessionProxyURL
delegate:self];
}
[fbSession logout];
}
self
may be used in a class method as a polymorphic Class instance.therefore, the class method
new
can be implemented like this:and would return the correct type for the Class instance that is messaged:
ex a:
ex b:
see note below.
So what you are really faced with is ensuring there are only instance methods in the protocol, and that (Class) self's methods map out to adopt the instance methods in the protocol.
As far as the design... well, let's just say I would not have written it this way. A singleton would have been clearer and more correct, but I don't even like singletons so I would not have taken that route.
The warning is produced because the Class instance (what is passed) does adopt the
@protocol
specified by thedelegate
parameter. TheClass
instance is not an instance of the class. A protocol declaration really applies to instances of the class. For example, if you adoptNSLocking
, does the compiler expect you to also implement class methods for every instance method declared in the protocol? Answer: Never. The implementation you're dealing with is IMO one of those cases where it's a misuse of the language, but it happens to work.To clarify the terminology:
The "
Class
instance" isself
in a class method:An "instance of the class" is
self
in an instance method:In practice,
-[NSObject conformsToProtocol:]
is+[NSObject conformsToProtocol:]
and+[NSObject class]
just returnsself
so there are no errors at execution.The criteria I described applies to the execution, but it deviates from the semantics of the language -- thus, the compiler is absolutely correct on this.
For a resolution to the problem: There's no way to tell the compiler "My Class instance conforms to protocol" because declaration of adoption applies to instances of the class.
You have two primary options:
The clean, correct approach: Use an instance of the class and implement the protocol as defined.
or Typecast the class instance to the protocol:
id delegate = (id)self; fbSession = [FBSession sessionForApplication:SHKFacebookKey getSessionProxy:SHKFacebookSessionProxyURL delegate:delegate];
If you choose #2, it may help to define the instance methods of the protocol to use class methods, like so:
that would also imply you never need instances. It would help because it will add warnings if the protocol will change. These warnings would bubble up to class methods when you follow the convention.
To reduce noise, you may also consider creating some method to reduce the typecast to one location:
then you can just write:
(note that the implementations of these types are actually class clusters, and you will see something different in the debugger or if printed)