NSOperationQueue(ASINetworkQueue) issue while uploading in iOS - ios

Using NSOperationQueue to upload multiple files to server.
//Class B
While uploading multiple files to server at the time move to popviewcontroller (class A). App suddenly crashed.
Is there any way to uploading a files to server without interrupt.
Thanks in Advance
//Class B
-(void)UploadtoS3
{
// Convert file to data from locapathfile here
NSData* imgData = [NSData dataWithContentsOfFile:localFilePath]; //VIDEO FILEPATH OR IMAGEPATH
if(![self upload_NetworkQueue]) // If Queue is not initialized
{
[[self upload_NetworkQueue] cancelAllOperations];
[self setUpload_NetworkQueue:[ASINetworkQueue queue]];
[[self upload_NetworkQueue] setDelegate:self];
[[self upload_NetworkQueue] setRequestDidFailSelector:#selector(upload_RequestFailed:)];
[[self upload_NetworkQueue] setQueueDidFinishSelector:#selector(upload_RequestDone:)];
[[self upload_NetworkQueue] setShowAccurateProgress:YES];
[[self upload_NetworkQueue] setMaxConcurrentOperationCount:1];
}
NSString *s3keyPath = [NSString stringWithFormat:#"/test123/%#",fileName];
NSLog(#"UPLOAD IMAGE S3 FILE NAME ----------- %#",s3keyPath);
request = [ASIS3ObjectRequest PUTRequestForData:imgData withBucket:testBuck key:s3keyPath];
[request setSecretAccessKey:s3SecretKey];
[request setAccessKey:s3AccessKey];
[request setTimeOutSeconds:20];
[request setDelegate:self];
[request setNumberOfTimesToRetryOnTimeout:3];
[request setMimeType:mimeType];
[request setUploadProgressDelegate:self];
[[self upload_NetworkQueue] addOperation:request];
[[self upload_NetworkQueue] go];
}

As the other poster suggested, you should move away from ASI's code. It is no longer supported.
As to your problem, though. My guess is that the code you posted is in a view controller. (Class B in your post) You are setting the view controller as the delegate of your networking class.
Then you are popping from the view controller that initiated the download to another view controller. That causes the view controller that requested the uploads to be deallocated. Thus, when the download queue tries to send a notification to it's delegate, you crash.
In general you should not make a view controller manage application global things like downloads. It would be better to create a singleton class that manages your downloads for you. If you're not familiar with the singleton design pattern then do a google search. There are lots of tutorials online explaining how to set up a singleton in iOS/Objective-C.

Related

iOS app crash recover method

Since 2 yrs I have been trying different ways to find the solution of app crash while click back button.
My application scenario:
In a tableview contoller I have to load list of users, On view did load I call getData(Asyncronous download) API method to load data. At the time of data download, If user press back button my application gets crash due to null value objects. That says all of my variable memory deallocated.
To overcome this problem, I used some loading indicator which lock UIScreen untill data download.
Questions:
Is there any alternatives to prevent crash, UIScreen Lock
Other applications use Activity Indicator in Menu bar without UIScreen Lock. How they are doing?
Need help to recover this issue
Here is my sample code to download data :
Below code doesnt crash app. But it download data even I cancel operations on dealloc
viewDidLoad:
ShowNetworkActivityIndicator();
_processQueue = [[NSOperationQueue alloc] init];
_processQueue.maxConcurrentOperationCount = 4;
_processQueue.name = #"Events Processing";
[self loadData];
loadData:
-(void)loadData
{
[_processQueue addOperationWithBlock: ^ {
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
[request setURL:[[NSURL alloc] initWithString:#"https://restcountries.eu/rest/v1/all"]];
NSURLResponse *response;
NSData *urlData=[NSURLConnection sendSynchronousRequest:request returningResponse:&response error:nil];
NSString *data=[[NSString alloc]initWithData:urlData encoding:NSUTF8StringEncoding];
NSDictionary *search = [NSJSONSerialization JSONObjectWithData:[data dataUsingEncoding:NSUTF8StringEncoding] options:NSJSONReadingMutableContainers error:nil];
[[NSOperationQueue mainQueue] addOperationWithBlock: ^ {
_countryListArray=[search mutableCopy];
[self.tableViewSample reloadData];
HideNetworkActivityIndicator();
}];
}];
}
I tried cancelAllOperations in dealloc:
[_processQueue setSuspended:YES];
[_processQueue cancelAllOperations];
Can you try inserting the reload data is dispatch_async(dispatch_get_main(),void (^){}); callback , main thread , I think the reload happening in the background thread is crashing the app.
[[NSOperationQueue mainQueue] addOperationWithBlock: ^ { _countryListArray=[search mutableCopy]; dispatch_async(dispatch_get_main_queue(), ^{
[self.tableViewSample reloadData];
HideNetworkActivityIndicator();
});}];

Getting error in async calls if the View Controller is no longer exists

I have a remote service that i call and it processes the request asynchronously. When the data is returned, i'll refresh my local UI.
But sometimes when the View disappears and if asynchronous call is still in the queue then the app crashes with error EXEC BAD ACCESS (i.e. the object is already released) i.e.
My app crashes when the service returns but the ViewController is disposed.
Mainly i am getting error when calling [delegate respondsToSelector:#selector (methodName:)], after the the view controller is no longer exist.
May be i need to cancel all my asynchronous calls (running or waiting in queue) in viewWillDisappear. But i am not able to cancel the running calls.
I have already tried this but in viewWillDisappear my self.navigationController.delegate is already nil.
Edit:
Method to call service:
{
NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:[NSURL URLWithString:[NSString stringWithFormat:#“%#method_name”,Base_URL]]];
[request setRequestMethod:#"POST"];
[request setTimeOutSeconds:600];
[request setPostValue:userID forKey:#“id”];
[request setDelegate:self];
[request setDidFinishSelector:#selector(requestFinished:)];
[request setDidFailSelector:#selector(requestFailed:)];
[request startAsynchronous];
}];
[operationQueue addOperation:operation];
}
And my requestFinished method (where my app crashes)
-(void)requestFinished:(ASIHTTPRequest *)request
{
// some stuff
// It's working fine when I normally run my app but fails when I rapidly changes the View Controller.
if ([delegate respondsToSelector:#selector(gotResponseData:)]) // Here my app crashes
{
[delegate gotResponseData:responseDict];
}
}
Delegate property in .h file:
#property (nonatomic,assign)id <protocolName>delegate;
Mainly this app crashes when I quickly switches between View Controller.
I'll edit my question if needed.
Kindly provide me some guidance.
In your block operation you should use a weak reference to self so that if/when self is released the operation doesn't call an invalid reference.
Indeed, you shouldn't actually need the operation as you are starting an asynchronous process inside the operation anyway.
Your delegate property in self should also be weak as your code indicates the problem is that the delegate is using an unsafe unretained approach (because of where you say the crash occurs).
Generally you would only have one request running from self so you should maintain a reference to the request and cancel it if self is destroyed.

Are there any iOS libraries that allow a POST message to be sent asynchronously and reliably?

I'm wanting to send some data to a web service which takes POST data.
However, if the internet connection drops then I need to queue the data. I.e. cache it and send it at the next available opportunity.
Are there any iOS libraries out there that might help with this?
At the moment I'm planning to do this using ASIFormDataRequest and, if that fails, store the data using NSUserDefaults. Then, I assume I'd need to complete the process in the background by:
looking out for a connection using an NSOperation that flags up a connection with an NSNotification
read the data from NSUserDefaults
send this data
remove the data from NSUserDefaults
Doesn't seem like a huge amount of work but am I re-inventing the wheel here or is this the best way to proceed?
Please have a look at the AFNetworking Framework at Github, this may be what you are looking for: https://github.com/AFNetworking/AFNetworking
I usually end up using ASIFormDataRequest and use NSThread to detach a new thread as well for the call. I then make it call my own delegate/protocol once it's done:
ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:[NSURL URLWithString:#"some_url"]];
[request setUseKeychainPersistence:YES];
[request addRequestHeader:#"Content-Type" value:#"application/json"];
for (NSString *key in dictionary) {
[request setPostValue:[dictionary objectForKey:key] forKey:key];
}
[request startSynchronous];
if([request responseStatusCode] == 200)
{
NSString *response = [NSString stringWithFormat:#"%#", [request responseString]];
if ([self _isValidDelegateForSelector:#selector(requestSucceeded)]) {
[_delegate requestSucceeded];
}
if ([self _isValidDelegateForSelector:#selector(itemAdded:)]) {
[_delegate itemAdded:[response JSONValue]];
}
}
else {
if ([self _isValidDelegateForSelector:#selector(requestFailedWithError:)])
NSLog(#"%#", [request debugDescription]);
[_delegate requestFailedWithError:[request error]];
}
This is all done in my own 'API' object that contains all the calls to the webs service, and hence why I called startSynchronous instead of asynchronous as I handle this in a different thread anyway.

iOS: sendSynchronousRequest with performSelectorInBackground

I need to make several https calls to a certain url. Therefore I do something like this
//ViewController Source
-(IBAction) updateButton_tapped {
[self performSelectorInBackground:#selector(updateStuff) withObject:nil];
}
-(void) updateStuff {
// do other stuff here...
NSMutableURLRequest* request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:self.url]];
[request setHTTPMethod:#"POST"];
NSData *postData = [[Base64 encodeBase64WithData:payload] dataUsingEncoding:NSASCIIStringEncoding];
[request setHTTPBody:postData];
NSURLResponse* response = [[NSURLResponse alloc] init];
NSData* data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:nil];
//Process the recieved data...
//Setup another synchronous request
data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:nil];
//Process data
//do this another 4 times (note for loop cannot be use in my case ;) )
//Finally update some view controllers
[[NSNotificationCenter defaultCenter] postNotificationName:#"NotificationIdentifier" object:self];
}
So the problem with this code is that it crashes randomly (not always but frequently). I get no debugging output on the log. Sometime my whole app freezes or it simply crashes the whole program. But it never crashes if I run it on the main thread. Therefore I think the code is correct and I suppose now that it has something to do with the threading on the iphone.
What problems could happen when running the code this way and what might cause a random crashes?
Memory Management, you don't release your request or response objects after allocation.
Please re-check your code so you don't update any GUI in background thread. Also, it should be much better to use asynchronous processing.
The controllers or whatever are probably assuming they're receiving notifications on the main thread, which in your case they're not (and that's never a safe assumption to make). Have the controllers dispatch back to the main thread in their notification callbacks before they do anything with the data/updating the UIKit stuff, etc.
You should also put an #autorelease block around your entire implementation of -updateStuff.
Here's an example of a callback notification you might receive in one of your controllers:
- (void)updateStuffNotificaiton:(NSNotification*)note
{
// Can't assume we're on the main thread and no need to
// test since this is made async by performSelectorInBacground anyway
dispatch_async(dispatch_get_main_queue(), ^{
// relocate all your original method implementation here
});
}
Also note that if your implementation of -updateStuff is creating and manipulating data structures that your notification callback methods then access, it is important to properly guard those accessors. It's often better to pass the data wholesale back to the callbacks in the notification's userInfo dictionary.
An example of adding the autorelease notation to your -updateStuff method:
-(void) updateStuff
{
#autoreleasepool {
// do other stuff here...
NSMutableURLRequest* request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:self.url]];
[request setHTTPMethod:#"POST"];
// rest of method snipped for brevity
//Finally update some view controllers
[[NSNotificationCenter defaultCenter] postNotificationName:#"NotificationIdentifier" object:self];
}
}

ASIHTTP Request is not working and application is crashing when i tried to release the Object of my Class

In my Main class i am creating an object of my AlbumFetcher class and calling some functions and relasing the object .
AlbumFetcher *_albumFetcher = [[AlbumFetcher alloc] init];
[_albumFetcher getData];
[_albumFetcher release];
When i relaesed the object after calling some functions , ASIHTTP request finish method is not calling and application is crashing . But when i am not releasing the object everything is working perfect . What i have to do
AlbumFetcher *_albumFetcher = [[AlbumFetcher alloc] init];
[_albumFetcher getData];
//[_albumFetcher release]; // now ASIHTTP Request n everything is working fine .....
In AlbumFetcher Class i have this functions ...
-(void)getData{
_fullsizeURL = [NSURL URLWithString:#"http://mysite.com/site/alb_iphone"];
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:_fullsizeURL];
[request setDelegate:self];
[request setDidFinishSelector:#selector(albumrequestDone:)];
[request setDidFailSelector:#selector(albumrequestFailed:)];
[request startAsynchronous];
}
- (void)albumrequestDone:(ASIHTTPRequest *)request{
// here my code
}
- (void)albumrequestFailed:(ASIHTTPRequest *)request{
// here my code
}
So where i am going wrong and where i have to release the object .
In your case, ASIHTTPRequest works asynchronously, i.e. in another thread. So your request is not done after [_albumFetcher getData] finishes.
Your request is not finished until albumrequestDone:request or albumrequestFailed:request finished. My suggestion is put [self release] at the end of those two functions.

Resources