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.
Related
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.
In my h file I have
+(CCRenderTexture*) createStroke: (CCLabelTTF*) label
size:(float)size
color:(ccColor3B)cor;
In my m file I implemented this method and use it like
CCRenderTexture* stroke = [self createStroke:pause size:3 color:ccBLACK];
But it gives me a warning "method not found". Why?
Since the +createStroke is a class method, you can't call it on self. You should instead send that message to the CCRenderTexture class.
So, in your case, if self is of the CCRenderTexture type, you could just replace it with self.class. (So that when you will subclass, the overridden method will be called by the superclass)
If it is not, write something like:
CCRenderTexture* stroke = [CCRenderTexture createStroke:pause size:3 color:ccBLACK];
plus means it is a class method, so you need to use the Class Name instead of an instance of that class: [ClassName createStroke:pause size:3 color:ccBLACK]
You probably want to have an instance method, so put a minus instead of plus in your declaration.
Here is some more info on this topic: What is the difference between class and instance methods?
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
Is there any way I can test if a method exists in Objective-C?
I'm trying to add a guard to see if my object has the method before calling it.
if ([obj respondsToSelector:#selector(methodName:withEtc:)]) {
[obj methodName:123 withEtc:456];
}
There is also the static message instancesRespondToSelector:(SEL)selector
You would call it like this:
[MyClass instancesRespondToSelector:#selector(someMethod:withParams:)]
or like this:
[[myObject class] instancesRespondToSelector:#selector(someMethod:withParams:)]
This may be useful if you would like to call one constructor or another one depending on this (I mean, before having the instance itself).
Use respondsToSelector:. From the documentation:
respondsToSelector:
Returns a Boolean value that indicates whether the receiver implements or inherits a method that can respond to a specified message.
- (BOOL)respondsToSelector:(SEL)aSelector
Parameters
aSelector - A selector that identifies a message.
Return Value
YES if the receiver implements or inherits a method that can respond to aSelector, otherwise NO.
You're looking for respondsToSelector:-
if ([foo respondsToSelector: #selector(bar)] {
[foo bar];
}
As Donal says the above tells you that foo can definitely handle receiving the bar selector. However, if foo's a proxy that forwards bar to some underlying object that will receive the bar message, then respondsToSelector: will tell you NO, even though the message will be forwarded to an object that responds to bar.
Checking selectors with respondsToSelector is normally only for delegate methods. You shouldn't be using forwardInvocation or proxies for delegate methods. If you need to use respondsToSelector in other situations you might want to make sure that there isn't a more appropriate way to design your program.
Is it possible to override ONLY CERTAIN functions from an exisiting delegate, without ourself being a delegate totally?
I tried replacing the target IMP with mine, didn't work :'(
More detail:
+[SomeClass sharedDelegate]
-[sharedDelegate targetMethodToBeOverridden:Arg:] //OUR method needs to be called, not this
Method *targetMethod; // targetMethodToBeOverridden identified by class_copymethodlist magic
targetMethod->method_imp = [self methodForSelector:#selector(overriddenDelegateMethod:Arg:)];
NOT WORKING! My Method is not being called :(
You probably shouldn't be manipulating the Method struct directly. Use the runtime function instead. You'll need to #import the runtime header, but there's a nice method in there called method_setImplementation. It'll work something like this:
id targetObject = [SomeClass sharedDelegate];
Method methodToModify = class_getInstanceMethod([targetObject class], #selector(replaceMe:argument:));
IMP newImplementation = [self methodForSelector:#selector(overriddenDelegateMethod:Arg:)];
method_setImplementation(methodToModify, newImplementation);
This may not work for your specific case, since class_getInstanceMethod might not return the Method for a method defined by an adopted protocol, but this is the "proper" way to swizzle Method IMPs.