How to declare a class that conforms to a protocol as parameter type? - objective-c

Is there a way to give, as a parameter, a class that conforms to a certain protocol?
What I tried at first, with a bit of hope, was this:
-(NSString *) getKeyForMyProtocolClass(Class<MyProtocol>)aClass
But that causes
[aClass superclass];
to give the warning "Instance method 'superclass' found instead of class method 'superclass'". I get the same sort of warning for conformsToProtocol:.
Since it gives no such warnings when the parameter is (Class)aClass, it seems Class< MyProtocol> is not actually of the Class type.
I should not be sending NSObject< MyProtocol>, since I need to determine the right key according to the class as well as its superclasses, and only create and add a new object if nothing is set to that key yet.
I could check with conformsToProtocol, but then I'd have to return a nil value which is just messy. I'd prefer to stop the issue at compile time.
So in short, is there a type declaration for a class that conforms to a protocol?

You can just typecast your class object to prevent the compiler warning. I was able to do the following:
- (void)tempMethod:(Class<NSObject>)klass {
id a = [(Class)klass superclass];
NSLog(#"%#", a);
}
Since you know the type of the object(Class object) you're passing this should work fine.

Related

Is it safe to store id into real class pointer before checking type

I have a lot of code that looks like this:
id myObjectRaw = getObject();
if(![myObjectRaw isKindOfClass:[MyClass class]]) return nil;
MyClass * myObject = myObjectRaw;
...
Here id getObject() can return several kinds of object. However the above code feels clunky to me. Is it safe to write this?
MyClass * myObject = getObject();
if(![myObject isKindOfClass:[MyClass class]]) return nil;
...
The compiler doesn't complain, but I'm not sure that I'm not treading on undefined behaviuor if getObject returns an object not related to MyClass.
(And no, I can't use a super class, or interface, since I dont actually have control over all the classes that get returned.)
You can do it. Nothing undefined. The only danger is that if the type is wrong and you forget to check the type, it may crash due to unrecognized selector exception.
In compiled code, id, MyClass * and NSString * have no difference, they just a pointer to a ObjC object.
Both versions will work. The first feels clunky, but there are problems with the second one as well: Putting something into a variable of a specific type implies knowledge of its type, and checking the class of something that seems to be known already looks redundant. If someone (it might be you) looks at that code next year, he may find the class check superfluous and remove it.
I've been in a similar situation, and I went with a helper method that gives a properly typed result or nil, i.e.
-(Rectangle)getRectangleObject {
id data = getObject();
if ([data isKindOfClass:[Rectangle class]]) return data;
return nil;
}
This simplifies code and communicates the intention clearly.
If you need several different type checks, you can go with several methods, or pass the class to this helper method.
As long as all types of returned objects conform to NSObject protocol (Classes that inherit from NSObject class do) it is safe to use isKindOfClass: method.
So make sure getObject() method only returns objective-c classes that inherit from NSObject
EDIT
While compiler is fine with it, as #Eiko mentions someone reading the code will probably think the isKindOfClass: check is unnecessary. It is better to use the former code to let the reader know that getObject() might also return other types of objects.
When you use id myObjectRaw you are NOT defining what kind of object myObjectRaw is, thus the compiler won't know if MyClass * myObject = getObject(); is a valid operation or not. THe compiler assumes you know what you are doing. If getObject() returns an object that is different than MyClass or it's not a subclass of it your app may crash. This is a runtime error.
If getObject() returns different objects, you should be expecting at least one object of the kind of objects that can be returned. If need to handle different objects, you can always use if-else-if instructions like:
id myObjectRaw = getObject();
if([myObjectRaw isKindOfClass:[MyClass1 class]])
{
MyClass1 objectClass1 = myObjectRaw;
}
else if([myObjectRaw isKindOfClass[MyClass2 class]])
{
MyClass2 objectClass2 = myObjectRaw;
}
However, if the object returned is a MyClass2 object, and this class is a subclass of MyClass1 the first condition will be true. Therefore, the object will be saved as a MyClass1 object. If that's the case you need to establish priorities and put them accordingly in the nested if-else-if statement.

The best way to know an instance type from a class method of the same class?

I want to learn how to make class methods to return instancetype values instead of id.
Simple demonstration:
#implementation MyGenericManagedObject
+ (instancetype)existingObjectByObjectID:(NSManagedObjectID *)objectID {
return (__typeof([self alloc]))[managedObjectContext() existingObjectWithID:objectID error:nil];
}
Having this method written this way, it does work, but if I remove (__typeof([self alloc])) I begin getting "Incompatible pointer types casting 'NSManagedObject *' to type 'MyGenericManagedObject *'.
What is the right way to get instance type from inside a class method of the same class?
existingObjectWithID:error: is explicitly typecast to return NSManagedObject*. That, frankly, is a bug in the API and you should file it.
Because of that, you'll need the cast.
#implementation MyGenericManagedObject
+ (instancetype)existingObjectByObjectID:(NSManagedObjectID *)objectID {
return (id)[managedObjectContext() existingObjectWithID:objectID error:nil];
}
Just casting to id should work.

Calling methods on objects of type id

when I'm trying to call a method on an object of type id, i get a warning raised (method not found). Of course not, but isn't that the sense of an id object?
Is there a way to tell the compiler:
"You don't now the class of the object on which i am calling this method, but don't worry, i'm sure it does implement it!" ?
You could use performSelector?
And if you've got an object type id it's probably a good idea to use respondsToSelector as well :)
i.e.
if ([myObject respondsToSelector:#selector(dosomething:)])
myObject performSelector:#selector(doSomething:) withObject:#"hello"];
Yes, just use a variable of the proper class type. You can either perform an explicit cast when sending the message, or you can assign it to a variable of the correct type. In Objective-C, the type id can be implicitly cast to any Objective-C object pointer type:
id myId = ...;
// Option 1: Use a cast when sending the message
[(MyClass *)myId someClassMethod];
// Option 2: Assign to a variable
MyClass *myObj = miId; // Implicit cast in the assignment
[myObj someClassMethod];
You could cast your id object to the class you know that it is.
If you have an id instance named instanceA and you know that it is of ClassA you cast it accordingly
Class A *instanceACasted = (ClassA *)instanceA;
then call the method
[instanceACasted methodCall];

Calling a static method on a class

Given a method like the one below, that returns a Class...
-(Class)getClass
{
return [MyAwesomeClass class];
}
...how do I call a static method on that class? I tried this, but it didn't work...
Class theClass = [anInstance getClass];
[theClass someStaticMethod];
How should I call a static method on theClass?
Edit to add:
It seems I was doing the right thing, and something else was causing the crash. Now I need to figure out how to get rid of the warning that the method someStaticMethod isn't found. What should I cast theClass to?
You do it exactly the way you've written it, assuming the class in question responds to someStaticMethod.
If it isn't working correctly, then one of these is most likely the case:
You don't have the class you think
The class doesn't respond to the message
You declared the method incorrectly
You haven't imported the header where the method is declared
The method itself is buggy
It is an old question but i answer it for completeness. if you use id instead of Class it will work
id theClass = [anInstance getClass];
[theClass someStaticMethod];
Compiler will be happy with this dynamic typing but you must be sure that Class will respond to +someStaticMethod or it will crash at runtime

Passing class method as selector problem

I want to build a selector from a class method.
I'm doing it this way:
NavigationTreeActionHandler* handler=[NavigationTreeActionHandler self];
NavigationTreeNode* bombsNode=new NavigationTreeNode("Bombs","bigbomb.tif"
,handler,#selector(BigBombButtonPressed:));
I need to pass to NavigationTreeNode the target and the selector to the target method.
I try to get the target using the self property of the class object (Don't know if htis is the correct way to do it). Then I get the selector for the class method I want to call on the class.
Everything compiles ok but it fails when I use:
[[handler class] instanceMethodSignatureForSelector:selector];
I get a nil and don't really know why... could anybody help please?
A few suggestions:
[NavigationTreeActionHandler self] will work fine to get the class object, but I would declare handler as type id instead of NavigationTreeActionHandler*, because it's not a reference to an instance that type
[handler class] is redundant; handler is already the class object.
instanceMethodSignatureForSelector: is only going to return nil if handler does not implement the selector. Verify your spelling etc., and try throwing in an NSLog to verify what you're receiving:
NSLog("handler = %# sel = %#", handler, NSStringFromSelector(selector));
But I'm unclear on what you're trying to do with instanceMethodSignatureForSelector: in the first place. If you're just trying to call the class method, wouldn't [handler performSelector:selector]; do what you want?
It's likely you're using code that assumes that calling the method class will get an object's class, and thus uses [[handler class] instanceMethodSignatureForSelector:selector] to get the method signature for the selector on the object handler. While that works for normal objects, that does not work for class objects, because of the different meaning of the class method on class objects, since the class method +class overrides the instance method -class, and +class simply returns the object it's called on, instead of the class it's an instance of (a meta-class).
The solution is that you don't even need to get the class at all, as there is a method you can call on an instance to get its method signature:
[handler methodSignatureForSelector:selector]
This is a much shorter and simpler way of doing what was intended, and also works for class objects too.