I have created a class and this class has its own delegate protocol.
Inside that protocol, there's an optional method, declared like
#protocol myClassDelegate <NSObject>
#optional
- (void) myOptionalMethod;
#end
Inside the class I have a call to myOptionalMethod, in the form of
[delegate myOptionalMethod];
but as the method is optional, if I call this method on a delegate that has not implemented the method, it will crash.
So, how do I test to see if the method was implemented before calling it?
thanks.
This is pretty easy.
if([delegate respondsToSelector:myOptionalMethod]){
// You can now call this method without a crash
[delegate myOptionalMethod];
}
-respondsToSelector: is useful for individual methods, as others have posted here. For a stricter interpretation, you can see whether a class was declared as implementing a protocol with the -conformsToProtocol: method:
BOOL isAGrommet = [myObject conformsToProtocol: #protocol(Grommet)];
You should use the respondsToSelector method to determine if the delegate has the relevant method prior to calling the selector on the delegate.
For example:
if([delegate respondsToSelector:#selector(myOptionalMethod)]) {
[delegate myOptionalMethod];
}
Related
I have a certain scenario where I am trying to accomplish more generic approach of using delegate and calling a selector based on what selector is being set.
For example below is the code:
#protocol HttpRequestDelegate
#optional
- (void)testDrive:(NSData*)dataembedd;
#end
- (id)init {
self = [super init];
if (self) {
//Initialize it here.
self.HttpRequestdelegate = self;
}
return self;
}
HttpRequest *apiCaller = [[HttpRequest alloc] init];
NSLog(#"%#",apiCaller.HttpRequestdelegate);
[WLCC_ApiCaller executeAsync:apiCaller.HttpRequestdelegate :#selector(testDrive:) :[NSURL URLWithString:updateUrl] :wlcc_Get];
However at executeAsync call I am trying to call the selector which is test drive like:
[delegate performSelector:#selector(selector) withObject:responseData];
but it gives me an error of unknown selector, however when I notice the reference of the delegate its the same when initialized.
I am trying to utilize delegate perform selector in different class, is there anything else besides that I need to do here?
Thanks.
Define your protocol like this:
#protocol HttpRequestDelegate <NSObject>
There is an NSObject class and an NSObject protocol. All of your protocols should conform to the NSObject protocol. This gives you methods like respondsToSelector: and others.
I suppose it should be; as what it seems that you're implementing the functions in other and try to call from that place which not inherited the delegate and, possible miss that particular function implementation.
Normally, the delegate pattern is used so we can implement the function according to our need (or can say provide callback) but that class must conform that delegate.Have you checked for it?
Please elaborate more about your implementation as I think so many info are behind the curtains like : WLCC_ApiCaller executeAsync,etc.
I recently tried to subclass UITextField and set the delegate to myself (found this trying ti solve my problem: http://www.cocoabuilder.com/archive/cocoa/241465-iphone-why-can-a-uitextfield-be-its-own-delegate.html)
#interface MyObject :UITextField <UITextFieldDelegate>
#end
#implementation MyObject
-(id) initWithFrame:(CGRect) frame
{
if((self=[super initWithFrame:frame]))
{
self.delegate=self;
}
return self;
}
-(BOOL) respondsToSelector:(SEL)selector
{
NSLog(#"responds to selector");
return [super respondsToSelector:selector];
}
// Implement all the missing methods
#end
Calling a method defined on the interface results in an infinite recursion. I don't see anything in the Apple docs that defines how respondsToSelector is supposed to behave in the presence of a delegate.
The docs for respondsToSelector states the following:
You cannot test whether an object
inherits a method from its superclass
by sending respondsToSelector: to the
object using the super keyword. [..]
Therefore, sending respondsToSelector:
to super is equivalent to sending it
to self. Instead, you must invoke the
NSObject class method
instancesRespondToSelector: directly
on the object’s superclass
It seems that this could be the cause for your recursion problem. I don't know if the delegate stuff is even related. Just a guess though.
I am using a external library and one of my view controller is registering as delegate for a class in that framework. Now, at one place I want to execute some code on this delegate class. I am writing a method for that and calling it on my delegate.
Now, all works fine but I am getting a warning that this newly added method is not part of the protocol.
This is my Class:
#protocol MyExtendedDelegate <LibraryDelegate>
#optional
- (void)actionTaken;
#end
#interface MyController : UITableController <MyExtendedDelegate> {
}
#end
And inside my controller I am registering self as delegate for library controller
LibraryController *libController = [[LibraryController alloc] init];
libController.delegate = self;
Finally, This is the code in a separate class where I am calling this method:
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
if ([self.libraryController.delegate respondsToSelector:#selector(actionTaken)]) {
[self.libraryController.delegate actionTaken];
}
Here is the warning I am getting:
-- actionTaken not found in protocol
-- NSObject may not respond to actionTaken
I want to get rid of this warning. Any idea.
The property libraryController.delegate is defined in the external library to conform to LibraryDelegate. Try to downcast to MyExtendedDelegate before you call the method from your extended protocol.
if ([self.libraryController.delegate conformsToProtocol:#protocol(MyExtendedDelegate)])
{
id<MyExtendedDelegate> extendedDelegate = (id<MyExtendedDelegate>)self.libraryController.delegate;
if ([extendedDelegate respondsToSelector:#selector(actionTaken)])
{
[extendedDelegate actionTaken];
}
}
Write a new protocol that extends the old one, and conform to that, something like:
#protocol MyNewProtocol <OtherProtocol>
- (void) myCoolMethod;
#end
(void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
if ([self.libraryController.delegate respondsToSelector:#selector(actionTaken)]) {
[self.libraryController.delegate performSelector:#selector(actionTaken)];
}
Using performSelector instead of directly calling a method will remove warning for sure.
I had a method on my main view controller named "calculateThis".
This method was run, obviously, as
int newValue = [self calculateThis:myVariable];
when I run it from inside the view controller.
Then I created a static class and I need to run this method from there.
How do I reference this method from that class using just relative references, as super, superview, delegate, etc. I cannot use the class name defined on the delegate because this static class is used in several apps of mine.
I need to go up in the hierarchy, I imagine one level, and access the method there...
thanks.
Define your utility methods in a category on NSObject or related subclasses of NSObject.
Which you have done.
Adding (id)sender to your method will work. Then your method can reference the object that called it. Something like this.
+(int)calculateThis:(id)sender userInfo:(id)info;
then your call becomes.
int newValue = [NSObject calculateThis:self userInfo:myVariable];
If your intent is to create a class that you can use without initializing it, that's possible using class methods. For instance, if I want to make a class called MyClass with a doSomethingWith: method, I would define the following:
In MyClass.h:
#interface MyClass : NSObject {
}
+(void)doSomethingWith:(id)thisObject;
#end
In MyClass.m:
#import "MyClass.h"
#implementation MyClass
+(void)doSomethingWith:(id)thisObject
{
// Your code goes here.
}
#end
To reference this method in another class, you can use the class object for MyClass like so:
[MyClass doSomethingWith:#"Hello, World!"];
This isn't really a typical Cocoa or Cocoa Touch design pattern, but can be handy for things like calculations.
Are you talking about the superclass? If so, you use [super ...].
I'm trying to implement delegation for a class which should call it's delegate (if any), when special things happen.
From Wikipedia I have this code example:
#implementation TCScrollView
-(void)scrollToPoint:(NSPoint)to;
{
BOOL shouldScroll = YES;
// If we have a delegate, and that delegate indeed does implement our delegate method,
if(delegate && [delegate respondsToSelector:#selector(scrollView:shouldScrollToPoint:)])
shouldScroll = [delegate scrollView:self shouldScrollToPoint:to]; // ask it if it's okay to scroll to this point.
if(!shouldScroll) return; // If not, ignore the scroll request.
/// Scrolling code omitted.
}
#end
If I try this on my own, I get a warning that the method I am calling on the delegate was not found. Of course it was not, because the delegate is just referenced by id. It could be anything. Sure at runtime that will work fine because I check if it responds to selector. But I don't want the warning in Xcode. Are there better patterns?
You could let the delegate be of the id type that implements the SomeClassDelegate protocol. For this, you could in the header of your SomeClass (in your case TCScrollView), do something like this:
#protocol TCScrollViewDelegate; // forward declaration of the protocol
#interface TCScrollView {
// ...
id <TCScrollViewDelegate> delegate;
}
#property (assign) id<TCScrollViewDelegate> delegate;
#end
#protocol TCScrollViewDelegate
- (BOOL) scrollView:(TCScrollView *)tcScrollView shouldScrollToPoint:(CGPoint)to;
#end
Then you can from your implementation, just call the method on the delegate:
#implementation TCScrollView
-(void)scrollToPoint:(NSPoint)to;
{
BOOL shouldScroll = YES;
shouldScroll = [delegate scrollView:self shouldScrollToPoint:to]; // ask it if it's okay to scroll to this point.
if(!shouldScroll) return; // If not, ignore the scroll request.
/// Scrolling code omitted.
}
#end
Following up on the sample code in drvdijk's answer, there could be a problem if there is any chance that delegate could be nil when you call the delegate method.
The return value of a message sent to nil is nil (aka 0.0 aka 0 aka NO), so if delegate is nil,
[delegate scrollView:self shouldScrollToPoint:to]
will return NO, which might not be the desired behavior in your case. It's safer to check first:
if (delegate != nil) {
shouldScroll = [delegate scrollView:self shouldScrollToPoint:to]
}
Also, if you don't want to see a compiler warning when sending messages declared by NSObject to your delegate (such as respondsToSelector:), include the NSObject protocol in your protocol declaration:
#protocol TScrollViewDelegate <NSObject>
- (BOOL) scrollView:(TCScrollView *)tcScrollView shouldScrollToPoint:(CGPoint)to;
#end
Use [NSObject performSelector:]
[delegate performSelector:#selector(scrollView:shouldScrollToPoint:) withObject:self withObject:to];
You won't get the compiler warnings anymore.
Alternatively create a prototcol and declare MyProtocol *delegate in header file.