NSURLConnection creating connectionTestFunction - objective-c

I'm currently using NSURLConnection to test if I can successfully connect to a server. Up until now, everything has worked flawlessly. The delegate methods get called, I can do what i want. But when I want to set a BOOL called connected in.
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
self.connected = YES;
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
self.connected = NO;
}
I always get NO.
I'm using my connection class in another class. I access my variable by doing this.
Connection *connection = [[Connection alloc] init];
[connection connectTo:#"localhost"];
connection.connected;<------ this is always NO.
Thanks

Your connectTo method is being called asynchronously. Meaning, after calling connectTo, the execution will proceed immediately to the next line where you check for your connected property. It won't wait for the connection to receive a response.
Also, you may want to take a look at Apple's Reachability class, which is provided for exactly this sort of connection testing:
http://developer.apple.com/library/ios/#samplecode/Reachability

Related

How to handle different requests using connectionDidFinishLoading in the same delegate?

Whenever I do a curl call using the below code:
NSURL *url = [NSURL URLWithString:requestURL];
NSURLRequest *request = [NSURLRequest requestWithURL:url
cachePolicy:NSURLRequestReloadIgnoringLocalCacheData
timeoutInterval:30];
if (connectionInProgress) {
[connectionInProgress cancel];
}
connectionInProgress = [[NSURLConnection alloc]initWithRequest:request delegate:self startImmediately:YES];
connectionDidFinishLoading is my final destination where I can manipulate the response data and call my next methods to continue with the app . If I hard-code some specific tasks like
-(void)connectionDidFinishLoading:(NSURLConnection *)connection
{
NSXMLParser *parser = [[NSXMLParser alloc] initWithData:xmlData];
[parser setDelegate:self];
[parser parse];
[someLabel setText:parsedTextFromXMLData];
}
If I need to do another curl call to a different address, wouldn't someLabel setText always get re-set again? Is there a way to make this delegate function behave differently on each curl call? (btw, is connectionDidFinishLoading usually the right place to put the next step of codes?) If so then wouldn't it always get called again by the next curl call?
Have a look at this S.O. post for a recipe concerning NSURLConnection and multiple requests.The suggestion is doing something like this:
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
if (connection == firstConnection) {
// do something
}
else if (connection == secondConnection) {
// do something else
}
}
EDIT: the idea here is that connectionDidFinishLoading is a method of your own delegate (so you write it). In the delegate, you store the address of each connection you create; then, when the connection comes back with the data, you tell which connection it is by comparing its address to the one you stored in the delegate. -END EDIT
Another option you have is using the ASIHTTPRequest framework, which offers a request-based (as opposed to connection-based) delegation mechanism, so each request has got a delegate object to handle the result; or, in other words, the delegate receives a reference to the request, so you can easily tell which request result you are handling.
ASIHTTPRequest offers a bunch of advantages over NSURLConnection. You can read about them in this S.O. post.
There're 2 options to do this:
you can implement a separate class, that will be responsible for handling NSURLConnection delegate stuff and create a separate instance for each request
you can use NSObject key-value methods on NSURLConnection instance for setting up some tag, that will be checked in connectionDidFinishLoading: method
For me, option 1 will be a better approach

Help with making NSURLConnection in a custom class

Hey all, I'm trying to use Yahoo's PlaceFinder to do reverse geocoding for an app I'm making. Problem is I need to use the NSURLConnection to call to my database. So I decided to make a custom class that is initialized with the user's latitude and longitude, and only store a string variable containing the state the user is in.
Update the following code now works fine....
Here is the .h
#import <Foundation/Foundation.h>
#import "CJSONDeserializer.h"
#interface StateFinder : NSObject
{
NSString *userState;
NSURLConnection *connection;
}
-(id)initwithLatitude:(NSString *)latitude andLongitude:(NSString *)longitude;
#property (nonatomic, retain) NSString *userState;
#property (nonatomic, retain) NSURLConnection *connection;
#end
and the .m
#import "StateFinder.h"
#implementation StateFinder
#synthesize userState;
#synthesize connection;
-(id)initwithLatitude:(NSString *)latitude andLongitude:(NSString *)longitude
{
if(self = [super init])
{
NSString *lat = latitude;
NSString *lon = longitude;
NSString *stateURLFinder = [NSString stringWithFormat:#"http://where.yahooapis.com/geocode?q=%#,+%#&gflags=R&flags=J&appid=zqoGxo7k", lat, lon];
//NSLog(stateURLFinder);
NSURL *stateURL = [NSURL URLWithString:stateURLFinder];
NSURLRequest *request = [[NSURLRequest alloc] initWithURL: stateURL];
connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
[request release];
}
return self;
}
-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
NSLog(#"didReceiveResponse");
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
NSLog(#"didFinishLoading");
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
NSLog(#"didFailWithError");
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
// Store incoming data into a string
NSString *jsonString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(jsonString);
// Yes, this is incomplete, but I was waiting for the method to fire before going
// any further. This will at least show me the JSON data being returned from yahoo
// in string format so I can output it to the console via NSLog
}
- (void)dealloc
{
[userState release];
[connection release];
[super dealloc];
}
#end
This is the current code I'm using and it works fine. All I did was include the connectionDidFinishLoading and didFailWithError methods to the original code. With regards to the connection being released before it was made, I used the code above as is without the previously mentioned methods and neither didReceiveData/didReceiveResponse would hit. It wasn't until those 2 methods were included that the methods began getting called. Not sure how, not sure why, but that was the only change among all of those suggested that worked. Big thanks to #Jiva DeVoe , #XJones , #jlehr and #Aby for all the tips/hints/suggestions!
Actually, I advise that you definitely do not use sendSynchronousRequest. You should always try to use asynchronous networking unless the app you're developing is a command line app without a run loop.
I suspect your problem may be that you're releasing the connection immediately, so it's never getting a chance to run. You should add a member variable and keep it around until you've received a response or whatever.
Bonus tip:
Guessing you're probably doing this for either iOS or the Mac, and writing GUI apps. These apps have run loops. When you use synchronous networking like the prior answer suggests, you prevent that runloop from executing. This means that if the request takes longer than a few seconds, your iOS app will be killed by the OS, and your Mac app will appear non-responsive. Neither of these are good results.
You're sending a release message to the connection before it's had a chance to run; don't do that. With regards to JSON, the string you said was returned from the server is JSON. Were you expecting something else?
Get rid of the line:
[connection scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
Once you've initialized the connection with the request it runs automatically. You have other problems as well. Your implementation of the NSURLConnectionDelegate protocol is wrong. You need to incrementally add data received in connection:didReceiveResponse to an NSMutableData object. You can convert it to JSON in `connectionDidFinishLoading:'.
As for the memory leak, release the connection in connectionDidFinishLoading: or connection:didFailWithError: You are guaranteed to receive one but not both of them.
Everything you need to know is here
[EDIT: added NSURLConnection code sample]
// Attach this to the touchUpInside event of a UIButton
// Note that all objects will be autoreleased
// Note that you can comment out any or all of the NSURLConnection delegate methods
// and the request will execute
- (IBAction)initiateRequest
{
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL urlWithString:#"http://www.google.com"]];
NSURLConnection *connection = [NSURLConnection connectionWithRequest:request delegate:self];
}
- (void)connection:(NSURLConnection *)connection didRecieveData:(NSData *)date
{
NSLog(#"connection:didReceiveData");
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
NSLog(#"connection:didReceiveResponse:");
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
NSLog(#"connectionDidFinishLoading:");
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
NSLog(#"connection:didFailWithError:");
}
Thanks to XJones for the suggestion. I wasn't including the two methods didFinishLoading or didFailWithError once inserted into my code, both didReceiveResponse and didReceiveData both began to hit. Thank you all for the tips, advice and suggestions and hopefully this helps someone else out down the road.

Object release and allocation timing

The code for this question is too long to be of any use. But I'm pretty sure my problem has to do with releasing a class.
I have a helper class, ConnectionHelper.h/.m, that handles a NSURLConnection for me. Basically, I give it the URL I want and it returns the data (it happens to do a quick json parse on it too). It has a delegate which I set to the calling class (in this case: DownloadViewController). When it finishes the download, it calls [delegate didFinishParseOf:objectName withDictionary:dictionary];. Then in DownloadViewController I release ConnectionHelper and alloc a new one in order to download the next object.
My problem is, I do this once, and then it creates the connection for the second one, and then my program just crashes. After this call:
[[NSHTTPCookieStorage sharedHTTPCookieStorage] setCookieAcceptPolicy:NSHTTPCookieAcceptPolicyNever];
NSURLConnection *theConnection=[[NSURLConnection alloc] initWithRequest:theRequest delegate:self];
Then I don't think any of the following methods are called:
- (NSCachedURLResponse *)connection:(NSURLConnection *)connection
willCacheResponse:(NSCachedURLResponse *)cachedResponse
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
So am I right in that I'm not releasing something? When I release it the first time, the dealloc function isn't being called. Is there a way I can "force" it to deallocate? Do I need to force it to? I didn't think it would matter since I allocating a new ConnectionHelper for the new call. How else would they overlap / conflict with each other?
Thank you.
First of all you never force deallocate. If you follow the memory management rules closely then you shouldn't even have to worry about it. For the rules Look at:
http://developer.apple.com/mac/library/documentation/cocoa/Conceptual/MemoryMgmt/Articles/mmPractical.html#//apple_ref/doc/uid/TP40004447-SW1
If your app is crashing it sounds to me more like something is being released early. Probably the object which you are setting as the delegate for your connection. Last but not least have you looked at ASIHTTPRequest for handling connections? It's a CFNetwork wrapper, very simple to use and very powerful.
Hopes this helps.

NSURLConnection leak?

i have set up a nsurl which grabs the data from http.
when i run instrument, it says i have a leak NSFNetwork object.
and how do i release theConnection in (void)ButtonClicked? or it will be release later on?
- (void)ButtonClicked {
NSURLRequest *theRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:KmlUrl]
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:20.0f];
NSURLConnection *theConnection = [[NSURLConnection alloc] initWithRequest:theRequest delegate:self];
if (theConnection) {
// receivedData is declared as a method instance elsewhere
NSMutableData *receivedData = [[NSMutableData data] retain];
[self setKMLdata:receivedData];
} else {
// inform the user that the download could not be made
}
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
// append the new data to the receivedData
// receivedData is declared as a method instance elsewhere
[KMLdata appendData:data];
NSLog(#"didReceiveData");
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
// release the connection, and the data object
[connection release];
[KMLdata release];
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
// release the connection, and the data object
[connection release];
// receivedData is declared as a method instance elsewhere
[KMLdata release];
}
I finally found the answer for this.
The error in the above code (which by the way is the near-exact sample from the SDK docs) is not in the memory management code. Autorelease is one option, manual release is another. Regardless of how you handle your NSURLConnection object, you get leaks using NSURLConnection.
First up, here is the solution. Just copy these 3 lines of code directly into connectionDidFinishLoading, didFailWithError and anywhere else you release the NSURLConnection object.
NSURLCache *sharedCache = [[NSURLCache alloc] initWithMemoryCapacity:0 diskCapacity:0 diskPath:nil];
[NSURLCache setSharedURLCache:sharedCache];
[sharedCache release];
Credit to mpramodjain on http://forums.macrumors.com/showthread.php?t=573253 for the code.
The problem seems to be this – the SDK caches the requests and replies on the iPhone. Even it seems if your NSMutableURLRequest cachePolicy is set to not load the reply from the cache.
The silly thing is that it seems to cache a lot of data by default. I'm transmitting a lot of data (split into multiple connections) and started to get memory warnings, and finally my App died.
The docs we need are in NSURLCache (not NSURLConnection), they state:
NSURLCache implements the caching of
responses to URL load requests by
mapping NSURLRequest objects to
NSCachedURLResponse objects. It is a
composite of an in-memory and an
on-disk cache.
Methods are provided to manipulate the
sizes of each of these caches as well
as to control the path on disk to use
for persistent storage of cache data.
Those three lines have the effect of nuking the cache totally. After adding them to my App (GPS Log), my #living object count remains steady.
Hello have you test this delegate method ?
- (NSCachedURLResponse *)connection:(NSURLConnection *)connection willCacheResponse:(NSCachedURLResponse *)cachedResponse
{
return nil;
}
You can manage the cache more precisely.
"reset" NSURLCache *sharedCache can cause problems on other part of your code ?
This is a common question and is solved by the magic of [object autorelease]. In your code this would be as follows:
NSURLConnection *theConnection = [[[NSURLConnection alloc] initWithRequest:theRequest delegate:self] autorelease];
In this way, the object is automatically added to the "autorelease pool" and dealloc'd at the start of the next run loop after it is no longer referenced.
Hope that helps
Edit: Also, I don't see why you're needing to call -retain on your receivedData variable.
I am using the static method/autoreleased approach and it appears to work fine:
[NSURLConnection connectionWithRequest:theRequest delegate:self];
This way you don't even have to worry about releasing in the delegate callbacks. It turns out that the retain count of the connection is actually 2 (not 1) after it is alloc'd in the examples above, which changes the way I thought about this memory "leak."
#rpetrich I actually don't think you need to worry about the delegate being released before the connection is released. The connection retains it's delegate and the connection itself is actually retained by some sort of open connections queue. I wrote a blog post on my experiments with NSURLConnection on my blog:
"Potential leak of object" with NSURLConnection

Cocoa: Checks required for multiple asynchronous NSURLConnections using same delegate functions?

This is with reference to the StackOverflow question Managing multiple asynchronous NSURLConnection connections
I have multiple asynchronous HTTP requests being made at the same time. All these use the same NSURLConnection delegate functions. (The receivedData object is different for each connection as specified in the other question above. In the delegate, I parse the receivedDate object, and do additional operations on those parsed strings)
Everything works fine for me so far, but I'm not sure if I need to do anything to ensure correct “multithreaded” behavior.
Is it possible that more than two connections will use the delegate at the same time? (I would think yes)
If yes, how is it resolved? (Does Cocoa do this automatically?)
Do I need to have additional checks in place to ensure that each request is handled “correctly”?
I enhanced the Three20 library to implement asynchronous connections across multiple threads in order to fetch data even if the user was playing with the UI. After many hours of chasing down random memory leaks that were detected within the CFNetwork framework I finally root caused the issue. I was occasionally losing track of responses and data.
Any data structures which are accessed by multiple threads must be protected by an appropriate lock. If you are not using locks to access shared data structures in a mutually exclusive manner then you are not thread safe. See the "Using Locks" section of Apple's Threading Programming Guide.
The best solution is to subclass NSURLConnection and add instance variables to store its associated response and response data. In each connection delegate method you then cast the NSURLConnection to your subclass and access those instance variables. This is guaranteed to be mutually exclusive because every connection will be bundled with its own response and data. I highly recommend trying this since it is the cleanest solution. Here's the code from my implementation:
#interface TTURLConnection : NSURLConnection {
NSHTTPURLResponse* _response;
NSMutableData* _responseData;
}
#property(nonatomic,retain) NSHTTPURLResponse* response;
#property(nonatomic,retain) NSMutableData* responseData;
#end
#implementation TTURLConnection
#synthesize response = _response, responseData = _responseData;
- (id)initWithRequest:(NSURLRequest *)request delegate:(id)delegate {
NSAssert(self != nil, #"self is nil!");
// Initialize the ivars before initializing with the request
// because the connection is asynchronous and may start
// calling the delegates before we even return from this
// function.
self.response = nil;
self.responseData = nil;
self = [super initWithRequest:request delegate:delegate];
return self;
}
- (void)dealloc {
[self.response release];
[self.responseData release];
[super dealloc];
}
#end
/////////////////////////////////////////////////////////////////
////// NSURLConnectionDelegate
- (void)connection:(NSURLConnection*)connection
didReceiveResponse:(NSHTTPURLResponse*)response {
TTURLConnection* ttConnection = (TTURLConnection*)connection;
ttConnection.response = response;
ttConnection.responseData = [NSMutableData
dataWithCapacity:contentLength];
}
- (void)connection:(NSURLConnection*)connection
didReceiveData:(NSData*)data {
TTURLConnection* ttConnection = (TTURLConnection*)connection;
[ttConnection.responseData appendData:data];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
TTURLConnection* ttConnection = (TTURLConnection*)connection;
if (ttConnection.response.statusCode == 200) {
// Connection success
}
}
- (void)connection:(NSURLConnection *)connection
didFailWithError:(NSError *)error {
TTURLConnection* ttConnection = (TTURLConnection*)connection;
// Handle the error
}
Assuming you're launching all of the (asynchronous) connections on a single thread, then the delegate messages will all get posted in that thread's run loop. Therefore the delegate only needs to be able to deal with one message being handled at once; the run loop will hand one message off at a time. This means that while the order of the delegate messages is unknown and the next message could come from any connection object, there will be no concurrent execution of your delegate methods.
However, were you actually trying to use the same delegate object across multiple threads, rather than just using the asynchronous nature of the API, then you would need to deal with concurrent delegate methods.
Yes it's possible to have multiple connections. the notification object contains a pointer to the NSURLConnection that triggered the notification.
Internally I guess NSURLConnection listens to a socket and does something like this when it has data ready.
[your_delegate
performSelectorOnMainThread:#selector(connectionCallback:)
withObject:self
waitUntilDone:NO];
so you don't have to worry about it being multithreaded, NSURLConnection will take care of this. For simplicity I have written self, in the real world a NSNotification object is given.
You shouldn't have to do any checks related to multithreading.