I have several memory leaks(NO! see Update 1) in my App and they all come down to an asynchronous URLRequest. The code below gives me a memory leak, it seems like the 'data' is never released (The code below is logically not used in my app as it is a completely useless infinite loop, I just wrote it to show the memory leak. This makes the used RAM go from 5 to 20 MB in less than one second. My internet speed does match this [just for the record]):
- (void)start{
NSOperationQueue *oQC = [[NSOperationQueue alloc] init];
NSLog(#"a");
[NSURLConnection sendAsynchronousRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:#"http://www.online-image-editor.com//styles/2014/images/example_image.png"]] queue:oQC completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
NSLog(#"b");
[self start];
}];
}
I have also tried setting data = nil, but that did, like supposed, not work.
Does anybody have any Idea of how to avoid the memory leak and if this is the normal behavior of a NSURLConnection?
UPDATE 1:
This doesn't seem to be related to a memory leak but to a caching problem. (Thanks to #rdelmar, who saw the problem, but his solution doesn't quite work)
Based on this post I have tried creating a new 'Loader' class with this in it's .h file:
#import <Foundation/Foundation.h>
#interface Loader : NSObject <NSURLConnectionDelegate, NSURLConnectionDataDelegate>
#property NSMutableData *mData;
#property (nonatomic, copy) void (^returnBlock)(NSData *data, NSError *error);
- (id)initForWhateverWithURLString:(NSString*)urlString andHandler:(void (^)(NSData *data, NSError *error))handler;
#end
And this in it's .m file:
#import "Loader.h"
#implementation Loader
#synthesize mData;
- (id)initForWhateverWithURLString:(NSString*)urlString andHandler:(void (^)(NSData *data, NSError *error))handler{
self = [super init];
if (self) {
/*NSOperationQueue *oQC = [NSOperationQueue mainQueue];
[NSURLConnection sendAsynchronousRequest:mUR queue:oQC completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
[[NSURLCache sharedURLCache] removeCachedResponseForRequest:mUR];
handler(nil, nil);
}];*/
mData = [NSMutableData new];
self.returnBlock = handler;
NSMutableURLRequest *mUR = [[NSURLRequest requestWithURL:[NSURL URLWithString:urlString] cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:60] mutableCopy];
NSURLConnection *URLCon = [[NSURLConnection alloc] initWithRequest:mUR delegate:self];
[URLCon start];
}
return self;
}
- (NSCachedURLResponse *)connection:(NSURLConnection *)connection willCacheResponse:(NSCachedURLResponse *)cachedResponse{
return nil;
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data{
[mData appendData:data];
data = nil;
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection{
self.returnBlock(mData, nil);
mData = nil;
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error{
self.returnBlock(nil, error);
}
#end
I also implemented:
NSURLCache *sharedCache = [[NSURLCache alloc] initWithMemoryCapacity:0 diskCapacity:0 diskPath:#"nsurlcache"];
[NSURLCache setSharedURLCache:sharedCache];
Still, neither of these methods helps reducing the drastic RAM usage!
I don't think what you're seeing is a leak. I think the memory keeps increasing because the data is being cached. If you change the caching protocol of the request, the problem goes away (you're getting the default behavior, NSURLRequestUseProtocolCachePolicy, by using requestWithURL:)
- (void)viewDidLoad {
[super viewDidLoad];
self.req = [NSURLRequest requestWithURL:[NSURL URLWithString:#"http://www.online-image-editor.com//styles/2014/images/example_image.png"] cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:10];
[self start];
}
- (void)start{
NSOperationQueue *oQC = [NSOperationQueue mainQueue]; // this queue is where the completion block runs, so you should use mainQueue if you want to do any UI updating
NSLog(#"a");
[NSURLConnection sendAsynchronousRequest:self.req queue:oQC completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
NSLog(#"b");
[self start];
}];
}
After Edit:
After looking at this some more, I'm not sure why sendAsynchronousRequest: causes a rise in memory usage with time. I tested using NSURLSession (which is what we should be using now anyway), and that seemed to work without causing a memory increase.
- (void)viewDidLoad {
[super viewDidLoad];
NSString *urlString = #"http://www.online-image-editor.com//styles/2014/images/example_image.png";
self.req = [NSURLRequest requestWithURL:[NSURL URLWithString:urlString] cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:10];
[self start];
}
- (void)start{
NSLog(#"a");
NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[NSOperationQueue mainQueue]];
NSURLSessionDataTask *task = [session dataTaskWithRequest:self.req completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
self.counter ++;
NSLog(#"counter is: %ld", self.counter);
[self start];
}];
[task resume];
}
if you are not using ARC then change
[[NSOperationQueue alloc] init];
to
[[[NSOperationQueue alloc] init] autorelease];
and
[NSURLRequest requestWithURL:[NSURL URLWithString:#"http://www.online-image-editor.com//styles/2014/images/example_image.png"]]
to
[[NSURLRequest requestWithURL:[NSURL URLWithString:#"http://www.online-image-editor.com//styles/2014/images/example_image.png"]] autorelease]
Related
I am new to Objective C and iOS development in general. I am trying to create an app that would make an http request and display the contents on a label.
When I started testing I noticed that the label was blank even though my logs showed that I had data back. Apparently this happens because the the response is not ready when the label text gets updated.
I put a loop on the top to fix this but I am almost sure there's got to be a better way to deal with this.
ViewController.m
- (IBAction)buttonSearch:(id)sender {
HttpRequest *http = [[HttpRequest alloc] init];
[http sendRequestFromURL: #"https://en.wiktionary.org/wiki/incredible"];
//I put this here to give some time for the url session to comeback.
int count;
while (http.responseText ==nil) {
self.outputLabel.text = [NSString stringWithFormat: #"Getting data %i ", count];
}
self.outputLabel.text = http.responseText;
}
HttpRequest.h
#import <Foundation/Foundation.h>
#interface HttpRequest : NSObject
#property (strong, nonatomic) NSString *responseText;
- (void) sendRequestFromURL: (NSString *) url;
- (NSString *) getElementBetweenText: (NSString *) start andText: (NSString *) end;
#end
HttpRequest.m
#implementation HttpRequest
- (void) sendRequestFromURL: (NSString *) url {
NSURL *myURL = [NSURL URLWithString: url];
NSURLRequest *request = [[NSURLRequest alloc] initWithURL: myURL];
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *task = [session dataTaskWithRequest: request
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
self.responseText = [[NSString alloc] initWithData: data
encoding: NSUTF8StringEncoding];
}];
[task resume];
}
Thanks a lot for the help :)
Update
After reading a lot for the very useful comments here I realized that I was missing the whole point. So technically the NSURLSessionDataTask will add task to a queue that will make the call asynchronously and then I have to provide that call with a block of code I want to execute when the thread generated by the task has been completed.
Duncan thanks a lot for the response and the comments in the code. That helped me a lot to understand.
So I rewrote my procedures using the information provided. Note that they are a little verbose but, I wanted it like that understand the whole concept for now. (I am declaring a code block rather than nesting them)
HttpRequest.m
- (void) sendRequestFromURL: (NSString *) url
completion:(void (^)(NSString *, NSError *))completionBlock {
NSURL *myURL = [NSURL URLWithString: url];
NSURLRequest *request = [[NSURLRequest alloc] initWithURL: myURL];
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *task = [session dataTaskWithRequest: request
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
//Create a block to handle the background thread in the dispatch method.
void (^runAfterCompletion)(void) = ^void (void) {
if (error) {
completionBlock (nil, error);
} else {
NSString *dataText = [[NSString alloc] initWithData: data encoding: NSUTF8StringEncoding];
completionBlock(dataText, error);
}
};
//Dispatch the queue
dispatch_async(dispatch_get_main_queue(), runAfterCompletion);
}];
[task resume];
}
ViewController.m
- (IBAction)buttonSearch:(id)sender {
NSString *const myURL = #"https://en.wiktionary.org/wiki/incredible";
HttpRequest *http = [[HttpRequest alloc] init];
[http sendRequestFromURL: myURL
completion: ^(NSString *str, NSError *error) {
if (error) {
self.outputText.text = [error localizedDescription];
} else {
self.outputText.text = str;
}
}];
}
Please feel free to comment on my new code. Style, incorrect usage, incorrect flow; feedback is very important in this stage of learning so I can become a better developer :)
Again thanks a lot for the replies.
You know what, use AFNetworking to save your life.
Or just modify your HttpRequest's sendRequestFromURL:
- (void)sendRequestFromURL:(NSString *)url completion:(void(^)(NSString *str, NSError *error))completionBlock {
NSURL *myURL = [NSURL URLWithString: url];
NSURLRequest *request = [[NSURLRequest alloc] initWithURL: myURL];
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *task = [session dataTaskWithRequest: request
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
dispatch_async(dispatch_get_main_queue(), ^{
if (error) {
completionBlock(nil, error);
} else {
completionBlock([[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding], error);
}
});
}];
[task resume];
}
and invoke like this
[http sendRequestFromURL:#"https://en.wiktionary.org/wiki/incredible" completion:^(NSString *str, NSError *error) {
if (!error) {
self.outputLabel.text = str;
}
}];
Rewrite your sendRequestFromURL function to take a completion block:
- (void) sendRequestFromURL: (NSString *) url
completion: (void (^)(void)) completion
{
NSURL *myURL = [NSURL URLWithString: url];
NSURLRequest *request = [[NSURLRequest alloc] initWithURL: myURL];
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *task = [session dataTaskWithRequest: request
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error)
{
self.responseText = [[NSString alloc] initWithData: data
encoding: NSUTF8StringEncoding];
if (completion != nil)
{
//The data task's completion block runs on a background thread
//by default, so invoke the completion handler on the main thread
//for safety
dispatch_async(dispatch_get_main_queue(), completion);
}
}];
[task resume];
}
Then, when you call sendRequestFromURL, pass in the code you want to run when the request is ready as the completion block:
[self.sendRequestFromURL: #"http://www.someURL.com&blahblahblah",
completion: ^
{
//The code that you want to run when the data task is complete, using
//self.responseText
}];
//Do NOT expect the result to be ready here. It won't be.
The code above uses a completion block with no parameters because your code saved the response text to an instance variable. It would be more typical to pass the response data and the NSError as parameters to the completion block. See #Yahoho's answer for a version of sendRequestFromURL that takes a completion block with a result string and an NSError parameter).
(Note: I wrote the code above in the SO post editor. It probably has a few syntax errors, but it's intended as a guide, not code you can copy/paste into place. Objective-C block syntax is kinda nasty and I usually get it wrong the first time at least half the time.)
If you want easy way then Don't make separate class for call webservice. Just make meethod in viewController.m instead. I mean write sendRequestFromURL in your viewController.m and update your label's text in completion handler something like,
- (void) sendRequestFromURL: (NSString *) url {
NSURL *myURL = [NSURL URLWithString: url];
NSURLRequest *request = [[NSURLRequest alloc] initWithURL: myURL];
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *task = [session dataTaskWithRequest: request
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
dispatch_async(dispatch_get_main_queue(), ^{
self.responseText = [[NSString alloc] initWithData: data
encoding: NSUTF8StringEncoding];
self.outputLabel.text = self.responseText;
})
}];
[task resume];
}
I'm having trouble getting a simple implementation of NSURLCache working on iOS8. It's my understanding that once a shared cache is created, it automatically caches data requests with the proper cache policy. No configuration required unless you want to customize behavior. Is this correct?
I've included a simplified version of my code below. The cache is created in AppDelegate, and the TableViewController that needs the data uses the APICaller object to make the call. The request is using NSURLRequestReturnCacheDataElseLoad, as this information doesn't need to be updated frequently.
If I'm way off the mark here. What's the next step? The data received is 95KB.
AppDelegate:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
NSURLCache *URLCache = [[NSURLCache alloc] initWithMemoryCapacity:4 * 1024 * 1024 diskCapacity:20 * 1024 * 1024 diskPath:nil];
[NSURLCache setSharedURLCache:URLCache];
return YES;
}
TableViewController:
- (void)viewDidLoad {
APICaller *apiCaller = [APICaller alloc] init];
[apiCaller makeAPICallWithCompletionHandler:^(NSArray *result, NSError *error){
if (error) {
// Handle error
} else {
self.property = [result mutableCopy];
[self.tableView reloadData];
}
}
}
APICaller:
- (void)makeAPICallWithCompletionHandler:(void(^)(NSArray *result, NSError *error))completionHandler
{
NSString *urlString = [NSString stringWithFormat#"https://api.apiwebsite.com/json/query?key=#", API_KEY];
NSURL *url = [NSURL URLWithString:urlString];
NSURLRequest *request = [NSURLRequest requestWithURL:url cachePolicy:NSURLRequestReturnCacheDataElseLoad timeoutInterval:10];
NSURLSessionConfiguration *config = [NSURLSession defaultSessionConfiguration];
self.urlSession = [URLSession sessionWithConfiguration:config delegate:self delegateQueue:nil];
NSURLSessionDataTask *dataTask = [self.URLSession dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
if (error) {
NSLog(#"There was an error");
dispatch_async(dispatch_get_main_queue(), ^{
completionHandler(nil, error);
});
} else {
NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error];
NSSortDescriptor *sortByName = [NSSortDescriptor sortDescriptorWithKey:#"name" ascending:YES selector:#selector(caseInsensitiveCompare:)];
NSArray *sortDescriptors = [NSArray arrayWithObject:sortByName];
NSArray *sortedResult = [dict[#"result"] sortedArrayUsingDescriptors:sortDescriptors];
dispatch_async(dispatch_get_main_queue(), ^{
completionHandler(sortedResponse, error);
});
}
}];
[dataTask resume];
}
According to this post and this post NSURLCache is broken in iOS 8 when using NSURLSession.
According to the first post I linked, NSURLConnection still seems to work. I remember seeing in one of the comments that this was still the case as of early Feb, but I haven't had a chance to investigate myself.
Best of luck.
I am trying to call a web service. I tried this
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:#"http://192.168.75.1:8082/projectname/public/tests"]];
NSURLSessionConfiguration *configuration = [ NSURLSessionConfiguration ephemeralSessionConfiguration];
NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration];
NSURLSessionDownloadTask *task = [session downloadTaskWithRequest:request completionHandler:^(NSURL *localFile, NSURLResponse *response, NSError *error) {
if(!error){
NSLog(#"no error");
}else{
NSLog(#"error");
}
}];
[task resume];
}
as you see there are two nslog statements. I got the no error one.
when I call that web service from my safari, I got a simple string which is index printed in the browser, how can I see that string in my xcode please?
Thanks
you can implement the delegate method
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location;
EDIT
Try This
NSHTTPURLResponse *response = nil;
NSError *error = nil;
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:YOUR URL]];
NSData *respData = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
NSLog(#"~~~~~ Status code: %d", [response statusCode]);
//Print your recived data here..
NSString *str = [[NSString alloc] initWithData:respData encoding:NSUTF8StringEncoding];
NSLog(#"str: %#", str);
You can use the delegate methods. When a NSURLSessionDownlaodTask is completed, it's delegates will be called if your class confirmed to it.
-(void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location
you can get your response by parsing the data in that delegate method. It will tell you the location that the URLSession stores the downloaded result.
what I would do If I were you is:
NOTE: it is based on you said you receive a simple string only from your back-end. if that is not a simple string, you may need to revise the –connectionDidFinishLoading: method's body.
.h
#interface UIRandomViewController : UIViewController {
NSURLConnection *_urlConnection;
NSMutableData *_receivedData;
// ...
}
// ...
#end
.m
#implementation UIRandomViewController {
// ...
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
NSURLRequest *_request = [NSURLRequest requestWithURL:[NSURL URLWithString:#"http://192.168.75.1:8082/projectname/public/tests"]];
_urlConnection = [[NSURLConnection alloc] initWithRequest:_request delegate:self startImmediately:TRUE];
// ...
}
// ...
#pragma mark - <NSURLConnectionDelegate>
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
_receivedData = nil;
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
if (_receivedData == nil) _receivedData = [NSMutableData dataWithData:data];
else [_receivedData appendData:data];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
NSString *_receivedString = [[NSString alloc] initWithData:_receivedData encoding:NSUTF8StringEncoding];
// hello beautiful...
NSLog(#"received data : %#", _receivedString);
}
}
#end
This code loads a table view:
- (void)viewDidLoad
{
[super viewDidLoad];
//test data
NSURL *url =[[NSURL alloc] initWithString:urlString];
// NSLog(#"String to request: %#",url);
[ NSURLConnection
sendAsynchronousRequest:[[NSURLRequest alloc]initWithURL:url]
queue:[[NSOperationQueue alloc]init]
completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
if([data length] >0 && connectionError ==nil){
NSArray *arrTitle=[[NSArray alloc]init];
NSString *str=[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
arrTitle= [Helper doSplitChar:[Helper splitChar20] :str];
self.tableView.delegate = self;
self.tableView.dataSource = self;
[self fecthDataToItem:arrTitle];
[self.tableView reloadData];
NSLog(#"Load data success");
}else if (connectionError!=nil){
NSLog(#"Error: %#",connectionError);
}
}];
// arrTitle = [NSArray arrayWithObjects:#"ee",#"bb",#"dd", nil];
}
And it takes 10 - 15s to load. How can I make this faster?
.
Thanks Rob and rmaddy, problem is solve.
As rmaddy points out, you must do UI updates on the main queue. Failure to do so will, amongst other things, account for some of the problems you're experiencing.
The queue parameter of sendAsynchronousRequest indicates the queue upon which you want the completion block to run. So, you can simply specify [NSOperationQueue mainQueue]:
NSURLRequest *request = [NSURLRequest requestWithURL:url];
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
if([data length] > 0 && connectionError == nil) {
NSString *str = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSArray *arrTitle = [Helper doSplitChar:[Helper splitChar20] :str];
self.tableView.delegate = self;
self.tableView.dataSource = self;
[self fecthDataToItem:arrTitle];
[self.tableView reloadData];
} else if (connectionError!=nil) {
NSLog(#"Error: %#",connectionError);
}
}];
Or, if you where doing something slow or computationally expensive/slow within that block, go ahead and use your own background queue, but then dispatch the UI updates back to the main queue, e.g.:
[NSURLConnection sendAsynchronousRequest:request queue:[[NSOperationQueue alloc] init] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
// do something computationally expensive here
// when ready to update the UI, dispatch that back to the main queue
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
// update your UI here
}];
}];
Either way, you should always do UI updates (and probably model updates, too, to keep that synchronized) on the main queue.
Hi i am using following code to load the image using NSURLConnection SendAsynchronousRequest call for Tableview but it crashes for IOS 4.3 but same code works for IOS 5.
So can anyone please tell me what changes i have to do support for IOS 4.3
i have gone through below links but nothing worked for me.
NSURLConnection sendAsynchronousRequest:queue:completionHandler not working in iOS 4.3
Have a class called
imagefetcher.h
- (void)fetchImageForURL:(NSURL *)url atIndexPath:(NSIndexPath *)indexPath inTableView:(UITableView *)table;
imagefetcher.m
- (void)fetchImageForURL:(NSURL *)url atIndexPath:(NSIndexPath *)indexPath inTableView:(UITableView *)table {
// NOTE: url is just relative
// There is an issue on iOS 5 that causes the memory capacity to be set to 0 whenever a UIWebView is
// used for the first time. This will correct that issue.
NSLog(#"in fetchImageForURL %#",url);
if([[NSURLCache sharedURLCache] memoryCapacity] != URLMemoryCachSize)
{
[[NSURLCache sharedURLCache] setMemoryCapacity:URLMemoryCachSize];
}
NSURLRequest *request = [NSURLRequest requestWithURL:url
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:30.0f];
NSCachedURLResponse *cachedResponse = [[NSURLCache sharedURLCache] cachedResponseForRequest:request];
if (cachedResponse)
{
NSData *data = [cachedResponse data];
NSLog(#"from cache");
[self postImageCallbackWithTableView:table atIndexPath:indexPath forURL:url withImageData:data];
}
else
{
returningResponse:&response error:&error];
// NSLog(#"loading synchronously");
[NSURLConnection sendAsynchronousRequest:request
queue:fetcherQueue
completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
[self postImageCallbackWithTableView:table atIndexPath:indexPath forURL:url withImageData:data];
}];
// [self postImageCallbackWithTableView:table atIndexPath:indexPath forURL:url withImageData:data];
}
}
in tableview controller i am calling follwing method but it crsahes for IOS 4.3 but same works for IOS 5
tableviewcontroller.m
-viewdidload()
{
[NSURLConnection sendAsynchronousRequest:request queue:fetcherQueue completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
//[self postImageCallbackWithTableView:table atIndexPath:indexPath forURL:url withImageData:data];
UIImage *image = [UIImage imageWithData:data];
[self.images setObject:image forKey:index];
[table1 beginUpdates];
[table1 reloadRowsAtIndexPaths:#[index] withRowAnimation:UITableViewRowAnimationNone];
[table1 endUpdates];
}];
}
If you look at the documentation for sendAsynchronousRequest, it requires iOS 5. If you need to support iOS 4.3, you'll have to use connectionWithRequest:delegate: or initWithRequest:delegate: and then implement the NSURLConnectionDataDelegate methods (which, while a little more work, offers other advantages such as being able to monitor the progress or cancel the request if you need).
Or, as the answer provided at that other question suggests, write your own method that provides the sendAsynchronousRequest functionality but that actually calls sendSynchronousRequest.
Or, just replace your call with sendAsynchronousRequest:
[NSURLConnection sendAsynchronousRequest:request queue:fetcherQueue completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
// do something with `data`, `error`, and `response`
}];
With a call to sendSynchronousRequest that you'll perform on some NSOperationQueue queue. So, first, define a property for your operation queue:
#property (nonatomic, retain) NSOperationQueue *networkQueue;
And then initialize it, for example in viewDidLoad:
self.networkQueue = [[NSOperationQueue alloc] init];
self.networkQueue.name = #"com.domain.app.networkqueue";
self.networkQueue.maxConcurrentOperationCount = 4;
And then you can use that network operation queue to call sendSynchronousRequest:
[self.networkQueue addOperationWithBlock:^{
NSURLResponse *response = nil;
NSError *error = nil;
NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
// assuming you want to interact with your UI and or synchronize changes to your model, dispatch this final processing back to the main queue
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
// do something with `data`, `error`, and `response`
}];
}];
Bottom line, just replace your calls to sendAsynchronousRequest with methods, such as sendSynchronousRequest, that were available in iOS 4.3.