ASIHTTPRequest / ASIFormDataRequest - referencing request object within blocks under ARC - iphone

Very similar to this question, I am trying to convert a project that uses ASIHTTPRequest & ASIFormDataRequest to ARC.
In my view controller classes, I often refer to and use properties of the request object in the completion blocks (looking at the response code, response data etc):
__block ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:[NSURL URLWithString:SOME_URL]];
[request setCompletionBlock:^{
if([request responseStatusCode] == 200) ....etc
When converting to ARC I get the warning:
Capturing 'request' strongly in this block is likely to lead to a
retain cycle
What is the proper way to do this?
Another SO user notes in the previous thread that simply adding __weak may cause the request to be released before the completion of the block, which I believe to be true.
How can I properly reference these properties in completion/failure blocks under ARC?

(I read your comment to the other question)
After implementing a few more modules using ASIHTTPRequest, I learned that the best way was to keep a strong reference to your request object. In your case, you can do:
self.request = [ASIFormDataRequest requestWithURL:[NSURL URLWithString:SOME_URL]];
__weak ASIFormDataRequest *weakRequest = self.request; // __block directive not needed since we only access the instance's properties.
[self.request setCompletionBlock:^{
if([weakRequest responseStatusCode] == 200)
// ...
This way you can still control self.request even after you start the request (e.g. for cancelling). You can do self.request = nil; when you're ready to release your request, maybe inside your completion block or self.request's parent object's cleanup methods.
Update:
If you're targeting pre-iOS 5, then the common ground stands: use __unsafe_unretained instead of __weak. This is OK because looking at ASIHTTPRequest.m, the blocks are nil'ed out in its dealloc() (i.e. they shouldn't get executed). Although I haven't tested that yet, so make sure to still test with NSZombies enabled.
Note:
The only safe way to cancel an ASIHTTPRequest object is to call its clearDelegatesAndCancel method. I've been bitten by some nasty bugs when I was just using the plain cancel one.

If you're targeting iOS versions before 5.0, that do not include weak support:
__unsafe_unretained __block ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:url];

I've found this answer to be helpful: https://stackoverflow.com/a/7735770/133875
It says to use __unsafe_unretained as well as __block

Related

Variable released prematurely during asynchronous request

My memory-management and threading knowledge is very limited, so I may be missing something really basic. I've found a work-around for this problem, but it really, really bothers me that I can't see what's happening.
I have a class that makes two asynchronous HTTP requests, one for an XML configuration file and another for an image. Because there are two asynchronous requests going on in the same class, I'm reusing the same NSURLConnectionDelegate methods (maybe factor). I first asynchronously fetch the configuration file and extract two urls, stored as sponsorImage and sponsorUrl. Then I use the value of sponsorImage to asynchronously fetch image data. I've found, though, that after I've got my image (after the second asynchronous event has completed), sponsorUrl has been released.
I accidentally found that I can prevent sponsorUrl from getting released if I "do something to it" in the method where the image request is created -- and by "do something", I mean just that. Basically, my code looks like this:
- (void) loadImage
{
sponsorUrl = [sponsorUrl stringByAppendingString:#""];
NSURL *url = [NSURL URLWithString:sponsorImage];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
[request setValue:dateString forHTTPHeaderField:#"If-Modified-Since"];
NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
[connection start];
[connection release];
}
If I remove the first line of this method (where I'm adding "" to the url), the variable is retained. If I remove it, it gets released. This makes no sense to me at all.
I've tried declaring sponsorUrl with #propterty (nonatomic, retain); I've tried declaring sponsorUrl statically; I've tried adding [sponsorUrl retain] where I set it, but it doesn't make a difference. The only thing that works is "touching" the variable before making the second request.
What am I missing?
As your are using a convenience constructor the variable is autoreleased! Only if you are using methods like alloc, copy or new they are retained implicitly.
Secondly, by writing sponsorUrl = .... your are not using the generated setter but the generated instance variable. You will need to write self.sponsorUrl = #"Blah"; or [self setSponsorUrl:#"blah"] in order to have the setter retain the variable.
Indeed, it seems you have some issues with memory management.
It is pretty difficult to explain what is happening because you are not providing the full code that uses your variables. Just as an example, take the statement:
sponsorUrl = [sponsorUrl stringByAppendingString:#""];
what you do is assigning to sponsorURL a new value; the old value (the one you initialized the variable with in the first place, i.e., the one that got the retain you mention) is released (stringByAppendingString forges a new object); the new object that sponsorURL points to is an autoreleased object whose lifetime is not exactly known: we only know that at some point it will be freed (possibly at the next main loop iteration). So, by "touching" the variable, you are assigning a new value to it which has a lifetime starting with the point where you touch the variable... pretty unreliable, in any case.
My suggestion is the following:
define two properties in your class to handle sponsorURL and sponsorImage;
make them of the retain kind;
assign value to them only through their accessor methods, i.e, self.sponsorURL = [...]
make sure that any objects you assign to the properties are autoreleased objects (or else, do a release with the assignment).
If you provide more code, then it would be possible to review it more thoroughly, but if you follow the guidelines above, you will have no problems at all (approx...)

deleting delegate on dealloc without an instance variable

so i start a ASIFormDataRequest on my [viewDidLoad] in a UIViewController.
ASIFormDataRequest *detailRequest = [ASIFormDataRequest requestWithURL:url];
detailRequest.delegate = self;
[detailRequest startAsynchronous];
If my UIViewController gets released before my Request finishes, my app crashes.
If i add my ASIFormDataRequest as an instance variable for example
#property(nonatomic, retain) ASIFormDataRequest *detailRequest;
and nil the delegate on dealloc
-(void)dealloc {
if(self.detailRequest != nil) { self.detailRequest.delegate = nil; }
self.detailRequest = nil;
[super dealloc];
}
the app no longer crashes.
but i don't think it's necessary to create a instance variable just for this, especially if i have multiple requests.
is there a better way to do this?
I usually create an array and store all active requests in the array. When the request is completed I remove the request, and when the controller calls dealloc I cancel all of the requests and nil the delegate.
In order to release it you must have a pointer to it so yes, use an ivar. iars are not expensive.
By doing self.detailRequest = [ASIFormDataRequest requestWithURL:url]; I am guessing it is creating an autorelease object whose lifespan isn't bound to your controller class. If the creation and deletion of your object is bound to your controller, it's logical to use a instance variable.
More details about autorelease
You could do this:
detailRequest.delegate = [self retain];
and then call
[self autorelease];
In the ASIFormDataRequest callback method. That's what I generally tend to do, anyway.
That way, the request object retains its delegate for the duration of the request.
As this is the Asynchronous request so if you set delegate it means as soon as response comes your delegate methods will be called. Till that time your object should be alive to handle the response. So making it retain and releasing in the dealloc is fine and before than that you have to set delegate to nil. So that if response comes after releasing the method, framework should not be misguided to search for method of dead object.
To handle multiple request the best way is to create the array and number of objects you want to use. When you are done with the objects, in dealloc method iterate through each object and set delegate nil and release the object.

Another Delegate for ASIHTTPRequest Asynchronous?

How can I create a new file containing just it's own delegate so that I can make an ASIHTTPRequest with its own asynchronous ending, and something easy enough where I just need to set [request setDelegate:self]; to something like [request setDelegate:AlternateDelegate]; and just add an include at the begining of the document to reference the AlternateDelegate delegate
I know this question is old, but in case anyone comes across it:
#Hankweb seemes to be talking about using a request as its own delegate. There are certainly situations where this works. For example, I'm working on a project that uses ASIHTTPRequest to fetch JSON from a remote source and import it into a Core Data store.
This operation (literally, as ASIHTTPRequest is a subclass of NSOperation) is almost entirely self-contained; I have a custom request on a background thread using a streaming JSON parser to import objects into a NSManagedObjectContext, which, when saved, triggers a notification that I catch internally and pass to the main thread's context using performSelectorOnMainThread:waitUntilDone:.
I'm using ASIHTTPRequest's block support to accomplish this; in my custom initWithURL: method, I set up the relevant callbacks (dataReceivedBlock, completionBlock, failureBlock, etc.). The traditional delegation pattern (using the ASIHTTPRequestDelegate protocol) should also work, though.
One gotcha: you should make sure the request doesn't retain itself too many times, or else you'll end up with a memory leak. This can be easy to miss when using multiple threads, and especially when using blocks. Instead of:
- (id)initWithURL:(NSURL *aURL) {
//...
[self setCompletionBlock:^{
[self doSomething];
}];
//...
return self;
}
Use the __weak attribute (or __block if you're not using ARC) when referencing self within the blocks:
- (id)initWithURL:(NSURL *aURL) {
//...
__weak id blockSelf = self;
[self setCompletionBlock:^{
[blockSelf doSomething];
}];
//...
return self;
}
If you don't know why this is important, make sure to read Apple's guide to blocks in Objective-C, and the ASIHTTPRequest block API documentation.
A delegate for ASIHTTPRequest is just a standard objective C object. Just create a new class, include it's header, create/get the object and set the delegate to be that object.
Have you tried this and run into a problem? If so what is the problem?

iphone - stopping a method that is running?

i have a [self parseXMLFileAtURL:path] method. is there anyway of stopping it midway? like terminating the method.
Reason for doing is because im running an apache http server on one pc and if the server is not running, the app will 'hang' if this method is called. so i want to to something like terminating the method after a certain amount of seconds , 5s maybe. and display an alert message.
I'd have two suggestions... one, if you possibly can, use NSMutableURLRequest with NSURLConnection to retrieve the data; which gives you much better control over things like timeout.
NSError * error;
NSURLResponse * response;
NSMutableURLRequest * request = [NSMutableURLRequest
requestWithURL:[NSURL URLWithString:#"http://..."]
cachePolicy:NSURLRequestReloadIgnoringLocalCacheData
timeoutInterval:60.0];
// Not sure if you need this, but I frequently do POSTs as well, so whatever:
[request setHTTPMethod:#"GET"];
NSData * responseData = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
NSString * xml = [[[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding] autorelease];
(NB: You'll have to check the error response, I just omitted it for clarity)
Also, ideally (since in my example I use the synchronous method) this should be run on a background thread... but I found it much easier to run this on the background thread manually using "performSelectorInBackground:" and use the synchronous methods, than I did using the async methods. Keep in mind, you'll have to create your own auto release pool if you do that... but that's two lines, and it's super easy.
Short of that, it IS possible to terminate the method... you'd have to run it on a different thread, and kill the thread if it took too long... but really, the NSMutableURLRequest isn't so bad, and it already gives you the timeout options you're looking for.
The thread programming guide at: http://developer.apple.com/iphone/library/documentation/cocoa/Conceptual/Multithreading/CreatingThreads/CreatingThreads.html#//apple_ref/doc/uid/10000057i-CH15-SW2 talks about killing threads... and tells you (indirectly) how to do it... but if you simply kill the thread, you are almost guaranteed to leak something.
I'm pretty sure that you could try installing a signal handler with sigaction to handle SIGALRM and use the alarm function. There is, however, probably a better solution using the Cocoa framework. I'll leave this here, but it's probably not the easiest way.
A method is just a C(++) function, so there really no way to stop it.

Creating multiple NSURLConnections. How to identify which Async call

I am intending to create 2 requests using NSURLConnection. When the server responds and calls connectionDidFinishLoading it passes in the connection as the parameter, but how do I identify which connection is passed in?
Save both NSURLConnection objects as member variables of whatever delegate object you passed to connectionWithRequest:delegate:. Then you can just compare each of those to the NSURLConnection passed to connectionDidFinishLoading:, and respond appropriately:
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
if (connection == firstConnection) {
// do something
}
else if (connection == secondConnection) {
// do something else
}
}
Another slightly more object-oriented option would be to create two different delegate objects, each of which knows how to deal with each each type of connection. Then just pass the appropriate delegate when you create each connection. That way you don't need to check to see which connection you have, because each delegate will only receive connectionDidFinishLoading: for its own connection.
I prefer different delegates for each connection, too. Although it's a bit of overhead. Fortunately, you can simplify things by using blocks. It's a new feature that doesn't exist in standard SDK yet, but there is 3rd-party framework called PLBlocks that you can use already. Here is an article on how to use them, it also contains example for NSURLConnection.
This is the client code making HTTP request with block callback:
NSURLRequest *req = [NSURLRequest requestWithURL:[NSURL URLWithString:#"http://www.google.com"]];
[NSURLConnection sendAsynchronousRequest:req onCompletionDo: ^(NSData *data, NSURLResponse *res, NSError *err) {
NSLog(#"data: %ld bytes. res: %#, error: %#", (long)[data length], res, err);
[cell.activity stopAnimating];
}];
I used to create a custom wrapper around NSURLConnection, too, but I've now switched over to ASIHTTPRequest. This is a fantastic library providing much more flexibility and features than NSURLConnection. Have a look and give it a try, it's really worth it.
What I do in my projects is create a wrapper class for the connection. This way, you can keep a new instance for each connection you need, and maintain these classes in another manager class.
Something like [AsynchronousConnection initWithURL:delegate:selector:]
Then you can be ensure the right thing is called when the NSURLConnection is done/failed.
Please do not refer https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/NSURLConnection_Class/Reference/Reference.html
GO to the NSURLConnection.h file and you will find the following.
When created, an NSURLConnection performs a deep-copy of the
NSURLRequest. This copy is available through the -originalRequest
method. As the connection performs the load, this request may change
as a result of protocol canonicalization or due to following
redirects. -currentRequest can be used to retrieve this value.
Ultimately [connection currentRequest].URL absoluteURL might help.
Regards,
PRASANNA.