Brandon Zacharie
Brandon Zacharie

Reputation: 2420

Create an instance from a Class that conforms to a Protocol

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

Answers (3)

Mecki
Mecki

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

Wain
Wain

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

jackslash
jackslash

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

Related Questions