I've created a singleton to perform some server requests. However for some requests I need to get the results back. What's the best way to get them from the invoking classes (using the singleton) ?
This is the invoked method by the singleton when the data are received. Should I pass a target and selector to the singleton to perform a method of the invoker class ?
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
// do something with the data
//invoking passed selector here ?
// receivedData is declared as a method instance elsewhere
NSLog(#"Succeeded! Received %d bytes of data",[receivedData length]);
// release the connection, and the data object
[connection release];
[receivedData release];
}
thanks
Passing local notification inside the delegate would be better than receiving the selector of the invoker class. That local notification can handle by anyone who registers it and thus minimize the dependency.
Related
Showing what a novice I am with Objective C here. The second of these two methods is getting called by the method above. Though I have absolutely no idea where? I want to be able to wrap the part that calls the second method in an if statement to determine if the file did exist based on the returned Boolean. Example code would be appreciated, if anyone could also explain how this second method gets called that would also be fantastic.
-(void) queryResponseForURL:(NSURL *)inURL {
NSMutableURLRequest * request = [NSMutableURLRequest requestWithURL:inURL];
[request setHTTPMethod:#"HEAD"];
NSURLConnection * connection = [NSURLConnection connectionWithRequest:request delegate:self];
// connection starts automatically
}
-(BOOL)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response{
if([(NSHTTPURLResponse *)response statusCode] == 200){
NSLog(#"file exists");
return YES;
}else return NO;
}
It is getting called by the NSURLConnection object, so you can't intervene in the code that calls it. But you shouldn't need to - the purpose of a delegate method is to enable you to hook in to that inaccessible code.
The method signature is this, by the way:
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
It doesn't have a return value and you can't change the method signature, as it's part of the NSURLConnection object's protocol. Remember, you don't call this method, the connection object does. But you can respond to the method. Instead of attempting to return a BOOL, you can deal with your 'if file..' code inside this method, or call another method from it. You should bear in mind that this is triggered (asynchronously) when a response is confirmed to have been initiated - so you can check HTTP header info such as mime type - but it doesn't guarantee a completed data transfer (there are other delegate methods for that).
See also the developer guide to NSURLConnection
The second method is a delegate or callback method. The callback method is called when your connection instance receives a response from the server your connecting with. NSURLConnection has a few informal protocols on NSObject. This means that methods defined in these protocols will automatically be called when any subclass of NSObject has implemented these methods. Your subclass of NSObject has implemented one of these callback methods, therefore this method is called. Most delegates are declared as formal protocols and need to be explicitly defined in your classes in order to make use of them. The informal protocols of NSURLConnection are exception to the rule, though this is about to change in future versions of iOS.
P.S.: a protocol in Objective-C is essentially the same as an interface in Java / C#.
More info here: http://developer.apple.com/library/ios/ipad/#documentation/Foundation/Reference/NSURLConnectionDataDelegate_protocol/Reference/Reference.html
I guess you want to cancel the connection when you receive the wrong response? What you should do is the following:
Store your URLConnection in an ivar or property (personally I'd prefer a property).
In your -connection:didReceiveResponse delegate method, if the response status code isn't an appropriate value, cancel your connection. You can use your ivar or property for this purpose.
When using the NSURLConnection can I release it at the end of the method where it is instantiated?
NSURLConnection* connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
...
...
[connection release];
Then it continue with the asynchronous process, or do I need to retain it?
You need to release connection object when you are done.
Release this in NSURLConnection delegate methods -
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
-(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
You should hold the connection by "assign", the reason is that: you need to manage the connection for example:cancel it.
I really give you the advice to use convenience allocators whenever possible. The objects you get back are autoreleased meaning (in easy words) you won't have to worry about manual releasing or retaining. To answer your question with more then a rule of thumb: use
connectionWithRequest:delegate:
and you're good to go.
The best is to use connectionWithRequest:Delegate: so that it's autoreleased.
You should however also keep a pointer to your NSURLConnection until the connection end to have the opportunity to nil the delegate. Otherwise in the case where your connection does not return but your delegate gets deallocated before being called by the connection, it will crash. ( you'll get the error: message sent to deallocated instance)
So the best is to keep a pointer to your NSURLConnection and set the pointer to nil when the connection return. So in ConnectionDidFinishLoading
connection = nil;
Now if the delegate gets deallocated you can put something in the dealloc method or in viewDidUnload if your delegate is a viewController
if (connection != nil) connection.delegate = nil;
This blog offers a nice solution for handling multiple NSURLConnections: make a custom "CustomURLConnection" class that has an additional tag property.
http://blog.emmerinc.be/index.php/2009/03/02/custom-nsurlconnection-class-with-tag/
http://blog.emmerinc.be/index.php/2009/03/15/multiple-async-nsurlconnections-example/
Basically, he has simply added a tag property to the exsisting NSURLConnection:
CustomURLConnection.m
- (id)initWithRequest:(NSURLRequest *)request delegate:(id)delegate startImmediately:(BOOL)startImmediately tag:(NSString*)tag {
self = [super initWithRequest:request delegate:delegate startImmediately:startImmediately];
if (self) {
self.tag = tag;
}
return self;
}
then, later in the normal NSURLConnection loading methods, you can do:
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
//Log the connection’s tag
CustomURLConnection *ttttag = (CustomURLConnection *)connection; // **HERE**
NSLog(#”%#”, ttttag.tag);
NSMutableData *dataForConnection = [self dataForConnection:(CustomURLConnection*)connection];
[connection release];
}
So, that's where I'm having trouble. The way I see it, this is how things go:
I create a "connection+tag"
The first code snippet I posted above creates a regular "connection" (no tag), which will eventually call the the normal NSURLConnection methods like connectionDidFinishLoading. What happens to the tag at this point?
In the connectionDidFinishLoading method I'm able to cast the connection back into a "connection+tag", then find that missing tag information that had been discarded. How?
Maybe I'm just confusing myself, but it seems as if the tag was discarded when it starts down the normal NSURLConnection path. But then by casting it as the subclass, I'm again able to recover the tag property. Where did it live/go in the mean time?
Could someone with a better understanding of inheritance explain this to me?
With this code:
[[CustomURLConnection alloc] initWithRequest:... delegate:... startImmediately:... startImmediately tag:...];
you create an instance of CustomURLConnection. Now here is where your understanding is wrong: this CustomURLConnection object can freely call all methods of its superclasses but it will always remain a CustomURLConnection. The tag is always there.
The methods that are defined in the superclass such as initWithRequest:delegate:startImmediately: don't know about the tag but they don't have to, either. When the delegate method gets called:
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
the connection argument is the very same CustomURLConnection that you created yourself above. The type in the method signature is different but that doesn't matter; because you know that this connection is of the CustomURLConnection type, you can just cast the connection object to the correct type and access the new property. But even if you wouldn't do that, the tag would still be there all the time.
I'm not sure what you mean by:
The first code snippet I posted above creates a regular "connection" (no tag).
What you've done here is create a subclass of NSURLConnection. Anywhere you can use the latter, you can use the former. NSURLConnection* means "a pointer to an NSURLConnection* or a subclass of it." So the original object you created was a CustomURLConnection and it included an extra ivar. That ivar doesn't disappear just because intermediary users refer to it by its superclass.
I must be doing something wrong, but the Automatic Reference Counting docs don't give me a hint on what it might be. What I'm doing is calling a method with a block callback from inside a delegate method. Accessing that same delegate from inside the block results in a bad access. The problem is the object I'm passing - loginController which is sending the message to its delegate - is clearly not released, when I don't access it inside the block I can call the method multiple times without an issue. Here's my code:
- (void)loginViewDidSubmit:(MyLoginViewController *)loginController
{
NSString *user = loginController.usernameLabel.text;
NSString *pass = loginController.passwordLabel.text;
__block MyLoginViewController *theController = loginController;
[self loginUser:user withPassword:pass callback:^(NSString *errorMessage) {
DLog(#"error: %#", errorMessage);
DLog(#"View Controller: %#", theController); // omit this: all good
theController = nil;
}];
}
NSZombieEnabled does not log anything and there is no usable stack trace from gdb. What am I doing wrong here? Thanks for any pointers!
Edit:
I figured the problem has a bigger scope - the callback above is called from an NSURLConnectionDelegate method (the block itself is a strong property for that delegate so ARC should call Block_copy()). Do I need to take special measurements in this scenario?
Flow (the loginController stays visible all the time):
loginController
[delegate loginViewDidSubmit:self];
View Delegate
(method shown above calls the loginUser: method, which does something like:)
httpDelegate.currentCallback = callback;
httpDelegate.currentConnection = // linebreak for readability
[[NSURLConnection alloc] initWithRequest:req
delegate:httpDelegate
startImmediately:YES];
NSURLConnectionDelegate
- (void)connection:(NSURLConnection *)aConnection
didFailWithError:(NSError *)error
{
if (NULL != currentCallback) {
currentCallback([error localizedDescription]);
self.currentCallback = NULL;
}
}
And this is where I get the bad access, but ONLY if I access that loginController variable...
Set copy attribute to the property, or just call 'copy' method for the block.
- (void)loginUser:(NSString *)user withPassword:(NSString *)pass callback:(void (^callback)(NSString *))
{
callback = [callback copy];
The actual solution was that I had the block as a strong property, but it should have been a copy property! D'oh!
First "Solution":
I just found a way to prevent the bad access. As shown in my Edit above, the View Delegate forwards the block to the httpDelegate (an instance of another class), which in turn keeps a strong reference to the block. Assigning the block to a temporary variable and forwarding the temporary block variable solves the problem, for whatever reason. So:
This crashes on block execution, as described
httpDelegate.currentCallback = callback;
This works
MyCallbackType aCallback = callback;
httpDelegate.currentCallback = aCallback;
I'll accept this as the answer, if anybody has more insights I'm happy to revise my decision. :)
I figure what is happening there is that the loginController is dead right after calling its delegate. Therefore a crash occurs. Without more information I can think of possible scenarios only:
The block do not retains the loginController object (__block type modifier). If the block is executed asynchronously, the loginController might no longer be available if it was killed elsewere. Therefore, no matter what you want to do with it, you wont be able to access it inside the block and the app will crash. This could happen if the controller is killed after sending loginViewDidSubmit.
I think most likely this could be your situation: The loginController calls its delegate object. The delegate method ends up synchronously invoking the callback block that kills the controller. The controller is expected to be alive after invoking the delegate method. Killing it inside the delegate method, most likely will cause crashes to happen. To make sure this is the problem, simply nil the loginController in the delegate method and put an NSLog statement in the controller after calling the delegate, never mind the block, you will get a crash there.
Perhaps if you paste some code we could help more.
My best.
In order to use asynchronous http requests in objective c, you need to set a delegate to NSURLConnection. The problem is that I need to make multiple http requests, so having the same delegate (self) wont work.
What is the best way to go about this? Should I make a new delegate class for each http request? Are these delegates just NSObjects?
You have a few options. The two most most common are:
Make a new class for each connection (yes, a subclass of NSObject) and set them as delegates -- have them carry out whatever logic you need when the data is loaded
Set one class as the delegate and store references to all of your NSURLConnections. That way, when your delegate gets - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data called, you can test which NSURLConnection is being used (eg if ([connection == myConnection]) -- or whichever delegate method you're implementing)
What I do is make a class that will handle downloading a file, and notify me when it is done through a selector. I pass it a delegate, a selector and the Info it needs to perform the download.
- (void) downloadFileFrom:(NSString*) httpLocation respondAt:(SEL)selector on:(id)target withParam:(id)param
{
self.finishSelector = selector;
self.delegate = target;
self.responseParams = param;
}
the Class is its own NSURLConnection delegate. Therefore the instance is separated from the others that I may instantiate, And it handles creating its own result for me to work with. I hold onto the param object. which could be anything.
At the end of the download it does a performSelector: on the delegate. passing itself to the delegate.
if ([self.target respondsToSelector:self.selector])
{
[self.target performSelector:self.selector withObject:self.param];
}
then you can create an instance of the downloader and call your method... telling it where to reply to you.
MyDownloader downloader = [[MyDownloader alloc] init];
[downloader downloadFileFrom:#"http://www.mydomain.com/myimage" respondAt:#selector(myFileIsComplete:) on:self withParam: downloader];
[downloader autorelease];
another option is to create a #protocol for your class to respond at, and have you delegate conform to the responder.
That should work, but there is another option to consider. You could make a generic class that creates and calls the NSURLConnection provided they are common enough. Then keep an NSArray or NSDictionary of the classes. One for each connection.
Example: I have an app that needs to download several photos simultaneously. Therefore, I have a GetFlickrPhoto class. It has a custom init method that receives the URL and any other necessary info. Each individual class creates the NSURLConnection and can safely set the delegate to self
This helps keep things contained and very manageable/reusable.
To take it a step further:
The app I mentioned before, also needed to download JSON feeds. So I made a GenericDownload class that took in URL and asynchronously downloaded the NSData and then returned the NSData to the calling delegate via defined success/failure protocols. It didn't care what the NSData contained.
I remodeled GetFlickrPhoto to call GenericDownload and use the returned NSData for a photo. I then made a GetJSON class that also called GenericDownload and parsed the returned NSData into a JSON feed.
Takes a bit more time but in the end you will be glad for maintenance and future projects.