Warning while adding and using a new method in external library protocol - iphone

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.

Related

How does respondsToSelector behave when there is a delegate present?

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.

Extending a protocol defined in external lib [duplicate]

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.

First time using Protocols - Objective-C

This is my first time using Protocols in Objective-C, and I'm running into a trouble: Here's what I've got:
I have a ReportsReceiver.h:
#protocol ReportsReceiver
-(void)receiveData:(NSArray *)theData;
#end
I have a MyController.h:
#interface MyController : UIViewController<ReportsReceiver,UITableViewDelegate,UITableViewDataSource> {
}
#end
I have a MyController.m with the implemented method:
- (void)receiveData:(NSArray *)theData {
NSLog(#"received some data!");
}
And then I have a class AllUtilities.m with the declaration:
Protocol *receiverProtocol;
AllUtilities.m also contains a method to initialize the protocol:
- (void)initProtocol {
receiverProtocol = #protocol(ReportsReceiver);
}
And then later on in AllUtilities.m I make the call:
[receiverProtocol receiveData:anArray];
Which crashes the application with the error:
2011-01-07 11:46:27.503 TestGA[91156:207] *** NSInvocation: warning: object 0x9c28c of class 'Protocol' does not implement methodSignatureForSelector: -- trouble ahead
2011-01-07 11:46:27.504 TestGA[91156:207] *** NSInvocation: warning: object 0x9c28c of class 'Protocol' does not implement doesNotRecognizeSelector: -- abort
How can I fix this? Thanks!!
You should read the part about protocols in the Objective-C guide once more :) I think you don’t really understand how protocols work. This is what you want:
// DataProducer.h
#protocol DataConsumer
- (void) handleData: (NSArray*) data;
#end
#interface DataProducer
#end
// DataProducer.m
#implementation DataProducer
- (void) generateDataAndPassTo: (id <DataConsumer>) consumer
{
NSArray *data = …;
[consumer handleData:data];
}
// SomeController.h
#import "DataProducer.h"
#interface SomeController <DataConsumer>
#end
// SomeController.m
#implementation SomeController
- (void) requestData
{
// The producer is of type DataProducer.
// Where you get it is irrelevant here.
[producer generateDataAndPassTo:self];
}
- (void) handleData: (NSArray*) data
{
NSLog(#"Got data.");
}
#end
A protocol is, in essence, a contract that says, for example, "an object conforming to the ReportsReceiver protocol must implement the receiveData: method".
So, MyController.h promises that receiveData: will be present, and MyController.m fulfills the promise. So far so good.
Now, your receiver variable doesn't care exactly what type of object the receiver is, so long as it conforms to the ReportsReceiver protocol. The way you declare that is:
id<ReportsReceiver> receiver;
...and in your initialization you might say:
receiver = myController;
Then invoke it like:
[receiver receiveData:anArray];
Start with adding the NSObject protocol to your own protocol. The warnings you are getting are methods from NSObject.
#protocol ReportsReceiver <NSObject>
-(void)receiveData:(NSArray *)theData;
#end
When declaring an object that implements a protocol, it should be more like:
id<ReportsReceiver> receiverProtocol;
or
ReceiverClass<ReportsReceiver> *receiverProtocol;
in the case that you create an object (ReceiverClass) that implements the ReportsReceiver protocol.
You assign a class that implements a protocol in the same way you assign any other class:
ReceiverClass<ReportsReceiver> *receiverProtocol;
- (void)initProtocol {
receiverProtocol = [[ReceiverClass alloc]init];
}
The #protocol directive begins declaring a protocol, not casting to one. Check out the docs for how to use them.

How to call a method of a specific class from another class (created in this specific class)?

I created a TTModelViewController. in the createModel Method i created a TTURLRequestModel. after Loading content in the TTURLRequestModel i want to call a method in my TTModelViewController.
TTModelViewController
- (void) createModel {
requestModel = [[singlePostModel alloc] initWithId:#"54"];
}
- (void)didLoadModel:(BOOL)firstTime {
NSLog(#"loaded");
}
TTURLRequestModel (singlePostModel)
- (void)requestDidFinishLoad:(TTURLRequest*)request {
//doing something
[super requestDidFinishLoad:request];
}
first i thought "didLoadModel" gets called after requestDidFinishLoad was called, but its before.
So, how can i call a method in my TTModelViewController after request is finished loading?
is there a method that already does that and i only have to overwrite this? or something else?
thanks
// if knowbody knows how to do this with three20, anybody can tell me how to do this in general?
edit
the solution of the first post works fine now, but i still got a warning that the function i call to my object doesnt exist:
#interface TTModelViewController {
}
- (void)modelFinishedLoading;
#end
and now i call that method in my models class
[controller modelFinishedLoading];
at this point xcode throws a warning "no -modelFinishedLoading method found".
why? i implemented it in the interface and also in the implementation part of TTModelViewController.
does that have to do with this #class at my singlePostModel?
#class singlePostViewController;
#interface singlePostModel : TTURLRequestModel
The way I would do it would be to subclass TTURLRequestModel, which I suspect you've already done based on the name of your class (singlePostModel). In that subclass, add a member variable that you can use to point back to your TTModelViewController. Something like:
Class Definition:
class TTModelViewController;
#interface singlePostModel : TTURLRequestModel {
TTModelViewController *controller;
}
- (id) initWithId:(String *)id forController:(TTModelViewController *)controller;
#end
Implementation:
#implementation singlePostModel {
- (id) initWithId:(String *)id forController:(TTModelViewController *)mvc {
if (self = [super initWithId:id]) {
controller = mvc;
}
}
- (void)requestDidFinishLoad:(TTURLRequest*)request {
[controller callMyMethodHere];
[super requestDidFinishLoad:request];
}
#end
When you initialize it then, you would use:
- (void) createModel {
requestModel = [[singlePostModel alloc] initWithId:#"54" forController:self];
}
Unless you have another way to get to your TTModelViewController, like through the app delegate singleton, in which case, that could work just as well, as long as you don't mind the coupling.
For example, your app delegate has a reference to the main View Controller for the application because it is set up from the main NIB. That is easily accessible using
((MyAppDelegate *)[[UIApplication sharedApplication] delegate]).myMainViewController

How to implement delegation the right way?

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.