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
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.
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.
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
i'm getting this two messages:
warning: 'MainView' may not respond to '-switchToNoGridView'
Messages without a matching method signature will be assumed to return 'id' and accept '...' as arguments
1st here:
//GridView.m
#import "GridView.h"
#import "MainView.h"
#implementation GridView
-(IBAction)switchToNoGridView {
[mainView switchToNoGridView];
}
#end
2nd here:
warning: 'MainView' may not respond to '-goBack'
Messages without a matching method signature will be assumed to return 'id' and accept '...' as arguments
in this:
//NoGridView.m
#import "NoGridView.h"
#import "MainView.h"
#implementation NoGridView
-(IBAction)goBack {
[mainView goBack];
}
#end
how to avoid these warnings?
Have you declared switchToNoGridView and goBack in the class interface for MainView?
This warning means that the method signatures could not be found in the class of the instance that you are calling the method on; since message dispatch in Objective-C is done at runtime this is allowed, however a warning is shown.
I'm not quite sure from your code, but I think you're encountering one of two errors. Either:
You haven't declared the methods switchToNoGridView or goBack in the MainView class declaration. For Xcode to know that an object responds to a method, you have to include its definition in the class header file, like this:
#interface MainView : ParentObject {
// instance variables
}
// properties
- (void)switchToNoGridView;
- (void)goBack;
#end
This assumes you actually want to declare them as (void), of course - they can have return values, but since you don't do anything with the result of the call in your code, I'm assuming they're void. Or:
You meant to call MainView the class, not mainView the object. Since we can't see your property or instance variable definitions for GridView.h, nor can we see the current method declarations in MainView.h, it's possible that you have a static method +(void)switchToNoGridView and +(void)goBack declared on MainView, but you're calling it on an instance mainView of MainView. For example:
#interface AClass : NSObject { }
+ (void)doSomething;
#end
#implementation AClass
+ (void)doSomething {
NSLog(#"Doing something");
}
#end
#import "AClass.h"
#interface AnotherClass : NSObject {
AClass *aClass;
}
#property(nonatomic,retain) AClass *aClass;
- (void)doSomethingElse;
#end
#implementation AnotherClass
- (void)doSomethingElse {
[aClass doSomething]; // This will break at runtime
[AClass doSomething]; // but this won't
}
#end
Basically, it's possible you've confused class methods with object methods. Either way, you should check your MainView header file for the appropriate method definitions.