Calling a static method on a class - objective-c

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

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.

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

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.

Objective C custom class methods not being called

So I have this custom class with just a test method that does nslog. I am going to reuse this method many times in my app. The interface looks like this.
#interface TestViewController: UIViewController { CMImageMover * imageMover }
Then in the view did load I:
imageMover = [[CmImageMover alloc] init];
If I do:
[imageMover testMethod];
Right after the alloc and init it works in the viewDidLoad function but if I call it again from another function in the view controller nothing works and the class method does not get called.
What am I doing wrong here. Every other var I declare like NSArray/NSTimer, I do the say way and I am able to access and use it throughout my controller.
When you say "if I call it again from another function in the view controller nothing works" then first thing to check is what you are sending the testMethod. It could be nil, in which case nothing will happen. In objective C sending a message to nil does nothing. Add an NSLog to find out, e.g.
NSLog(#"imageMover object is: %#", imageOver);
[imageMover testMethod];
If the NSLog shows it is nil - or something crazy - then follow up what you are doing with the imageMover ivar.
You mention a class method in your question, but don't refer to it in your code snippets.
If you have defined testMethod as a class method it will, of course, fail if you send that message to an instance. (And it will fail noisily.) A class method would be introduced like this:
+ (void) testMethod
{
NSLog(#"CMImageMover testMethod called on Class");
}
An instance method would be introduced like this:
- (void) testMethod
{
NSLog(#"testMethod called on an instance of CMImageMover");
}
Apologies if this is all screamingly obvious to you and missing the point of the question. It's not that clear from your question where the issue lies.

Objective-C: How to check if a class gets extended by a category?

In Java, you can use instanceof to check if a class extends another class or implements an interface.
In Objective-C, you can use isKindOfClass to check if a class extends another class:
if ([myObject isKindOfClass:[AnClass class]]) { }
But how can I check if a class gets extended by a category?
EDIT 2
My code of the first EDIT was unfortunately a bit confusing and nonsensical, sorry! Now, here is my new code:
I'll explain the whole problem:
I've got a class ViewCustomerCreate thats extends UITableViewController. ViewCustomerCreate gets extended by the category ICheckBox. This is my code that doesn't work:
- (void)closeModalView {
UINavigationController *parent = (UINavigationController *)self.navigationController.parentViewController;
UIViewController *parentViewContr = parent.topViewController;
if ([parentViewContr isKindOfClass:[id<ICheckBox> class]]) { // ERROR-MESSAGE see below
id<ICheckBox> parent2 = (id<ICheckBox>)parentViewContr; // works fine :-)
[parent2 setSelectedElementId:checkedIndex]; // works fine :-)
}
[self.navigationController dismissModalViewControllerAnimated:YES];
}
The error message is: "error: 'id' is not an Objective-C class name or alias"
I think that I can't use isKindOfClass to check if a class gets extended by a category, isn't it?
PS: What do I want? I have a general modal view with checkboxes and if I close this view, the parent-view should get informed what the user choose.
EDIT 3
OMG, I confounded Category with Protocol!! Aaaaahhhhh ^^
THE SOLUTION:
if ([parentViewContr conformsToProtocol:#protocol(ICheckBox)]) {
There is no way to check if a class is extended by a category, but you can check whether or not an instance responds to a particular selector with:
- (BOOL)respondsToSelector:(SEL)sel;
In Objective-C you should worry less about what an object is, and worry more about what an object can do.
If it walks like a duck, sounds like a duck and looks like a duck, then it can probably fly, you know what I mean?
You should use this as such:
if ([myObject respondsToSelector:#selector(myMethod:)])
{
// do whatever you need to do
}
Just a quick note, since you mentioned Java interfaces. You can check if an object implements a protocol (similar to Java interfaces, but not exactly the same) by using:
- (BOOL)conformsToProtocol:(Protocol *)aProtocol;
If you have defined a category on UIViewController, there are no instances of UIViewController that it is not applied to. Hence, a runtime check does not make sense.
Let's look at your actual problem:
parent.setMySpecialValue = 1; // DOES NOT WORK :-(
What does "DOES NOT WORK" actually mean? Do you get a compiler error or a runtime error. If the former, there are a couple of possible issues to loo at:
You haven't included the header file containing the category in the source file that uses that method
It is a property that you have named incorrectly. If the property is called mySpecialValue, that line of code should read:
parent.mySpecialValue = 1;
or
[parent setMySpecialValue: 1];
As of now, categories cannot define instance variables, so having a synthesized property might be an issue, so that might also be your problem, but you need to give more information about what "DOES NOT WORK" means.

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.