Getting Xcode to drop "No XXX method found" warning when delegating - iphone

This could be me doing the design pattern wrong.
I'm implementing asynchronous delegation in an application that uses NSURLConnection. An object wraps the NSURLConnection and handles its delegated messages; that works fine. Now I'm defining my own delegates in the object that uses it (NSURLConnection messages ConnectionWrapper, ConnectionWrapper messages NeedsToUseConnection, you get the idea), and that works as well, however, Xcode emits this warning:
No '-request:finishedWithResult' method found
This is, presumably, because I'm declaring the delegate I'm calling like this:
id<NSObject> delegate;
...and Xcode is checking what NSObject declares in the Foundation framework. My custom delegate message is not there. I am properly insulating the call:
if([delegate respondsToSelector:#selector(request:finishedWithResult:)])
[delegate request:self finishedWithResult:ret];
Aside from turning the warning off -- I like to work with as many warnings on as possible -- is there a way to communicate (either syntactically or via a compiler directive) that I am aware this message is undeclared? Should I, instead, be using an interface design pattern for this รก la Java? Using id<WillReceiveRequestMessages> or something?
Open to suggestion.

A better way of doing it would be to create your own delegate protocol:
#protocol MyControlDelegate <NSObject>
#optional
- (void)request:(MyControl *)request didFinishWithResult:(id)result;
#end
Then, you would declare your delegate like this:
id <MyControlDelegate> delegate;
The compiler will no longer complain when you write this:
if ([delegate respondsToSelector:#selector(request:didFinishWithResult:)])
[delegate request:self didFinishWithResult:result];
The <NSObject> syntax is important in the protocol definition because it tells the compiler to incorporate the NSObject protocol. This is how your protocol gets methods like respondsToSelector:. If you left that out, the compiler would start complaining about respondsToSelector: instead.

This is, presumably, because I'm declaring the delegate I'm calling
like this: ...and Xcode is checking what NSObject declares in the
Foundation framework.
That is incorrect. If that were the case then you would get a warning about the object "may not respond to" the method, or something like that. This is a completely separate problem.
This warning is due to the fact that the compiler must know the signature of a selector in order to call it. This is because, behind the scenes, the compiler translates a method call to either objc_msgSend or objc_msgSend_stret depending on whether the method returns a struct type or not. If it doesn't know the return type, it will guess that it is not a struct, and use the first function. However, this could be wrong.
The solution is to have the method declared anywhere at all. It doesn't even have to be declared in the right class. You can declare it in some dummy protocol that is never used. So long as it is declared somewhere, the compiler will know and will be able to correctly compile it.

Related

Requiring the presence of a method in an id

The Situation
My custom controller class has the following method:
- (void)requestViewControllerWithIdentifier:(NSString *)identifier fromObject:(id)object;
This causes object to receive this message:
- (UIViewController *)viewControllerWithIdentifier:(NSString *)identifier;
The Problem
Right now, object is just an id so there's no guarantee that it actually implements that method, and I get a compiler warning when I send the message.
The Solution
I came up with two solutions myself:
Use a protocol.
Make id an NSObject and create a category for NSObject.
They are both fine solutions probably and I don't mind choosing one of them, but...
The Question
...I noticed Apple is doing something odd in their GameKit API. GKSession has the following method:
- (void)setDataReceiveHandler:(id)handler withContext:(void *)context
handler is just an id, but Apple actually requires it to implement this method:
- (void) receiveData:(NSData *)data fromPeer:(NSString *)peer inSession: (GKSession *)session context:(void *)context;
Without making use of any protocol or category! I'm wondering how and why would they do this? Why don't use a protocol? Do they enforce the method in some other way? If I were to do this, how can I suppress the compiler warning?
You can assign a variable of type id to any object type. If you know that it must implement a given protocol you can assign it to a variable of that type within the method and invoke the method on that variable.
As a design point, I would say that it is better to make the protocol explicit and externalise it to the caller so that the compiler can do type checking properly. Some parts of Apple's code are better than others at this: from what you say GameKit is very much at the unhelpful end of things.
Defining a category is not what you want to do, because that tells the compiler that you will add the method to every NSObject.
If you have this protocol:
#protocol YourProtocol <NSObject>
- (UIViewController *)viewControllerWithIdentifier:(NSString *)identifier;
#end
And define your method as:
- (void)requestViewControllerWithIdentifier:(NSString *)identifier fromObject:(id <YourProtocol>)object;
It will probably do what you want. It may not be strictly necessary in this case but it's usually a good idea to have your protocol extend the NSObject protocol (as above) so that you can call useful stuff like -retain, -release, -autorelease, and -respondsToSelector. The alternative of declaring the method as follows prevents the user from using an NSProxy-rooted object as the object parameter.
- (void)requestViewControllerWithIdentifier:(NSString *)identifier fromObject:(NSObject <YourProtocol> *)object;
If it's just for your own use and you aren't using proxy objects this can be quite convenient, but you should avoid it in public APIs.
I think the answer you seek is the difference between a formal and an informal protocol. This answer has a good explanation.

Compiler gives warning on performSelectorOnMainThread:#selector(delegateMethod)

I have an NSOperation that wraps some web service functionality. The NSOperation has a delegate that is to be messaged when the operation is over.
As the NSOperation lives on a different thread I have to make the call like this:
[delegate performSelectorOnMainThread:#selector(getDealersIDSuccess:) withObject:result waitUntilDone:YES];
It works just fine, but it gives me a warning:
warning:
'-performSelectorOnMainThread:withObject:waitUntilDone:'
not found in protocol(s)
I completely agree with the compiler on this one, it sees a delegate, it checks the protocol, it finds no declaration of a performSelector method.
My question is: can I remove the warning by making this call in a different manner?
My two guesses are that I could (1) write a method called
- (void) callDelegateMethodOnMainThred {
[delegate getDealersIDSuccess:result]
}
and call that through performSelectorOnMainThread, but I find that solution to be cumbersome and an extra, hard to read, step on top of the delegation.
The second solution could be to cast the delegate to the type of my parent object inside the selector, but that is just plain crazy and goes against the delegate encapsulation pattern.
I would really appreciate a third solution from someone with a better understanding of the language:)
Thank you in advance.
EDIT: Added delegate declaration:
id <ISDealersIDDelegate> delegate;
I declare my delegate as id. The delegate it self extends UIViewController.
I could see that declaring it NSObject would work.
performSelectorOnMainThread:withObject:waitUntilDone: method is declared in NSObject class. If your delegate object inherits from NSObject you can declare it as
NSObject<MyDelegateProtocol> *delegate;
So compiler will know that delegate responds to NSObject's methods and won't issue a warning.
It might be even a better solution not call performSelectorOnMainThread: on a delegate or other protocol implementation.
Make it the responsibility of the delegate/receiver to determine if it needs to do things on the main thread.
[delegate performSelector:#selector(delegateAction:)
withObject:actionData];
Delegate implementation
- (void)delegateAction:(NSData*)actionData
{
[self performSelectorOnMainThread:#selector(updateUIForAction:)
withObject:actionData
waitUntilDone:NO];
}
- (void)updateUIForAction:(NSData*)actionData
{
// Update your UI objects here
}
It might look like more code, but the responsibility is in the right place now
Actually on iOS 4 I prefer using NSNotifications and Observers (with Blocks) to perform updates on the mainthread.
- (id)addObserverForName:(NSString *)name object:(id)obj queue:(NSOperationQueue *)queue usingBlock:(void (^)(NSNotification *))block
I wrote down my design here.
1) Declare your delegate's protocol to extend the NSObject protocol in the .h file
#protocol YourDelegateProtocol <NSObject>
2) Cast to NSObject in your call
[(NSObject*)delegate performSelectorOnMainThread:#selector(getDealersIDSuccess:) withObject:result waitUntilDone:YES];
I definitely recommend number (1).

Unnecessary warnings in the code

I am not able to find out why am I getting unnecessary warnings like:
"Method 'someMethod' not found"? Though at run time it is executing this method and I am getting the desired results. FYI... The called method resides in separate class which i have already imported in my class.
Usually one of two reasons:
1) You haven't casted the object that you're calling that method on correctly.
[(UITableView*)myTableView setDelegate:self];
2) The method that you're calling may not be in your custom Class' (public) #interface
#interface MyCustomClass : NSObject {
}
- (void)doSomethingReallyImportant;
#end
If you are trying to do something to an object, did you cast your object to the object's class?
If you are trying to access a method in your implementation of a class, do you have that method declared in your .h?
Then you have probably not put that method in the class' #interface. You should if it is a public method.
Being able to compile without warnings is a good thing.

Call back style

I am writing an iPhone application which in numerous places needs to perform non HTTP or FTP networking of a very simple request response type.
I've wrapped all this up into a SimpleQuery class that integrates with the run loop.
SimpleQuery *lookup = [[SimpleQuery alloc] init];
[lookup setDelegate:self];
[lookup doQueryToHost:queryServer port:queryPort query:queryString ];
As you can see the calling object sets itself as a delegate. When the results are complete it then calls a method on the delegate with the results.
[delegate simpleQueryResult:resultString simpleQuery:self];
I am now in a position where I have a user of SimpleQuery that has two types of query so I need to extend SimpleQuery to support this.
I can think of two sensible ways of doing this.
Firstly passing a selector into doQueryString, or a seperate doQueryStringWithSelector.
[lookup doQueryToHost:queryServer port:queryPort query:queryString selector:#SEL ];
Secondly passing a tag into doQueryString so that when the delegate is called it can query the tag, as the simpleQuery is passed, to find out what the results are for.
[lookup doQueryToHost:queryServer port:queryPort query:queryString withTag:tag ];
I'm just wondering which is best from a coding style perspective, the first seems simpler but tagging seems more in keeping with the iPhone SDK and Interface Builder
An option which is used commonly in Apple's code (for example, in UIControl) is to provide both a target object and a selector. This works only if there is a single callback, and is more appropriate than a delegate in that case. (If there are multiple callbacks, then you'll probably have to go with a delegate and the tag approach.)
If you go this route, then you do away with the delegate altogether and instead have a method with a signature like this:
doQueryToHost:(id)queryServer port:(int)queryPort query:(NSString*)queryString target:(id)target action:(SEL)action
Note that "action" is typically preferred over "selector" in methods arguments in this case. The query would simply call the selector on the target when done. This would allow your clients to have multiple selectors, and also multiple target objects; this can help clean up code because you don't need to shove everything into a single delegate object.
If you want to go with your tag route, you should call it "context", which is what Apple uses (for example, in addObserver:forKeyPath:options:context).
There's a third option that's a common pattern in the kits, which is to use #protocols.
For example:
#protocol QueryCompleteHandlerProtocol
- (void)queryType1Complete:(int)intStuff;
- (void)queryType2Complete:(float)floatStuff;
#end
What this does is declare a set of method calls that an object adopting the protocol has to conform to (the compiler will actually enforce this).
So your SimpleQuery object will hold on to something like the delegate pointer, which you might declare like this among the ivars:
NSObject<QueryCompleteHandlerProtocol> *callback;
What this tells the compiler is that callback is an object that descends from NSObject and adopts the QueryCompleteHandlerProtocol protocol. Sometimes you see this written as:
id<QueryCompleteHandlerProtocol> callback;
When you want to call the callback there's nothing special about them, SimpleQuery's methods will just call:
[callback queryType1Complete:1];
[callback queryType2Complete:2.0];
Finally you client for the procotol class will declare itself as adopting the protocol:
#interface MyClass : NSObject<QueryCompleteHandlerProtocol>
...
#end
And will set itself as the callback with some code like:
[lookup setCallback:self];
This is where the compiler checks that MyClass conforms to QueryCompleteHandlerProtocol, meaning it has implemented queryType1Complete: and queryType2Complete:.
I'm not sure I understand the problem here. Can't SimpleQuery's user just set another delegate object for the second query, or branch on the simpleQuery: parameter? That's a basic part of the delegate pattern, just like having two UIActionSheets for one view controller.

How to use self class method on iPhone? (conceptual question)

I write an instance method in ClassName.m:
-(void)methodName:(paraType)parameter
{...}
And call it using [self methodName:parameter]; A warning will pop up, but the code still runs successfully.
Is this because I haven't created an instance of the class? Why the method still runs normally? And what is the correct way to call self method to prevent the warning?
Well the first step in receiving help with a warning would be to post the warning :)
I am assuming it is something about an unrecognized message? If so it's because although the compiler sees the call to "methodName" it does not know if that is valid for the object or not.
I would guess your code looks like;
-(void) someFunc
{
...
[self methodName:parameter];
...
}
-(void)methodName:(paraType)parameter
{
...
}
You can either;
a) Place the 'methodName' function earlier in the file so the compiler has seen it before it's used in calls.
b) declare it in the class interface. E.g.
// Foo.h
#interface Foo {
...
}
-(void) methodName:(paraType)parameter;
#end
What is the warning that you get?
Do you have a definition of the method in your header file?
The syntax you use is the propper way of calling method on self.
The method will work because Objective-C methods are resolved at run-time. I expect the warning you get is something like "Object Foo may not respond to -methodName:" and then it tells you that it's defaulting the return type to id. That's because the compiler hasn't seen a declaration or definition of -methodName: by the time it compiles the code where you call it. To remove the warning, declare the method in either the class's interface or a category on the class.
If you are getting a warning it might be because the method signature isn't in an interface.
#interface foo ....
-(void)method;
Once the implementation is written the warning should go away since it's not the first time the compiler has seen the method. It will work without doing this, but the warning message is annoying.