Reputation: 2420
I'm trying to accomplish something like the following:
- (id<SomeProtocol>)instanceFromClass:(Class<SomeProtocol>)cls
{
return [[cls alloc] initUsingSomeConstructorDefinedInProtocolWithValue:_value];
}
However, I'm getting a No Known class method for selector 'alloc'
error. How may I specify in my signature that I want to receive a class that conforms to a protocol? Or, if that part is correct, how may I create an instance from that argument using a constructor defined in the specified protocol?
Upvotes: 6
Views: 3560
Reputation: 132979
+ alloc
is a method defined by the top level class NSObject
. When you have a class like Class <SomeProtocol>
, the compiler only knows that this is some class and it implements SomeProtocol
but it cannot know if that is a subclass of NSObject
or not, since in Obj-C you can define own top-level classes that don't inherit from NSObject
(not that this is generally a good idea but it is possible).
There is a special "hack" in the compiler that in case the type is just Class
, the compiler will always assume that it is a subclass of NSObject
and would only fail at runtime in case it isn't. But this hack only works for the exact type Class
and not for Class <SomeProtocol>
which is a distinct type.
So what you can do is to either cast to Class, so the hack works again:
[[(Class)cls alloc] ...]
or you can also do that
[[cls.class alloc] ...]
in case that cls
will be a subclass of NSObject
at runtime because then it will have a + class
method.
Note that if I call instanceFromClass:
with a class, that does implement SomeProtocol
but is no subclass of NSObject
and also does not implement a + alloc
method, both methods above will fail at runtime and the app will crash.
Upvotes: 0
Reputation: 119031
Your use of the protocol is 'fine'. The issue is that the cls
parameter is tagged as a class which conforms to a protocol that defines instance methods (the init method). This doesn't tell the compiler that the +alloc
method is available because that is a class method on NSObject
.
You can add the +alloc
method to the protocol. Or you can do some casting to tell the compiler to trust you.
Upvotes: 2
Reputation: 8570
Not sure why the compiler complains but you can fix by casting your parameter back to Class
- (id<SomeProtocol>)instanceFromClass:(Class<SomeProtocol>)cls
{
return [[(Class)cls alloc] initUsingSomeConstructorDefinedInProtocolWithValue:_value];
}
while still getting you the type checking you want for the parameter as hinted at in this SO answer: Declare an ObjC parameter that's a Class conforming to a protocol
Upvotes: 8