threads is exiting my app - iphone

I'm trying to upload images to a php file, everything works, but sometimes when the site is
getting slow to load our app start frizzing until the page is done loading so i thought
about using threads to avoid such frizzing, but right now our app display and quit. I dont
see any warnings or problem... it just enter the app and exist right away. Please help me
out
the code:
-(void) source {
NSData *imageData = UIImageJPEGRepresentation(imageView.image, 80);
NSString *urlString = #"http://domain";
NSMutableURLRequest *request = [[[NSMutableURLRequest alloc] init] autorelease];
[request setURL:[NSURL URLWithString:urlString]];
[request setHTTPMethod:#"POST"];
our_label.text = [NSString stringWithFormat:#"%#", returnString];
}
-(void)som:(id)param{
[lock lock];
[lock unlock];
}

You can use the NSURLRequest in an asynch method, however if you want your current code to work follow these insturctions
First change this line
[NSThread detachNewThreadSelector:#selector(multithreading:) toTarget: [MainViewController class] withObject:nil];
to
[NSThread detachNewThreadSelector:#selector(multithreading:) toTarget:self withObject:nil];
Second, you are updating the UI in a non UI thread, so wrap this
our_label.text = [NSString stringWithFormat:#"%#", returnString];
around dispatch_get_main_queue like the following
dispatch_async(dispatch_get_main_queue(), ^{
our_label.text = [NSString stringWithFormat:#"%#", returnString];
});
Finally remove the locks since you are not really using them
//[lock lock];
[self source];
//[lock unlock];

Totally wrong approach. Look carefully at the documentation of NSURLConnection, and you will find the method sendAsynchronousRequest:queue:completionHandler:.
You will need to learn using blocks though (but it is worth it).

In addition to yan.kun's answer,
I suggest you to use third party library such as AFNetworking which has all the functionality to deal with Async operation which will be more easy to code and safe.
link : https://github.com/AFNetworking/AFNetworking/

Related

AFImageRequestOperation with SDWebImage issue

I use AFImageRequestOperation to download some icons, meanwhile use SDWebImage to download some pics for main view. Each AFImageRequestOperation is added to my publicOperationQueue defined in app delegate, its maxConcurrentOperationCount is set to 10. Strange thing is that sometimes one or two of my 10+ icons will be replaced by some pic in main view, which should be downloaded by SDWebImage. And when I set a larger maxConcurrentOperationCount which is bigger than my icon counts, it works fine. I doubt if it has something to do with multiple NSOperationQueues sharing some resources and maxConcurrentOperationCount. Any one could help?
//below is the icon downloading code
//============================//
for(NSString *url in picUrls)
{
NSMutableURLRequest *urlRequest = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:url]];
[urlRequest setHTTPShouldHandleCookies:NO];
[urlRequest addValue:#"image/*" forHTTPHeaderField:#"Accept"];
AFImageRequestOperation *requestOperation = [[AFImageRequestOperation alloc] initWithRequest:urlRequest];
[requestOperation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject){
NSString *imageName = trimNilOrNuLL([url lastPathComponent]);
if(imageName.length > 0)
{
NSData *imageData = UIImagePNGRepresentation(responseObject);
[imageData writeToFile:[path stringByAppendingPathComponent:imageName] atomically:YES];
}
} failure:^(AFHTTPRequestOperation *operation, NSError *error){
NSLog(#"%#",error);
}];
[[AppShare appDelegate].publicOperationQueue addOperation:requestOperation];
}
//============================//
and for SDWebImage, I use - (void)setImageWithURL:(NSURL *)url method in UIImageView+WebCache category to download pic
OK,From the question found that you are using AFNetworking .Then for image downloading why dont you use the UIImageView Extention of AFNetworking?
You dont have to implement any queue or anything for this
some code like this is enough i think
[self.imageview setImageWithURL:url placeholderImage:nil];

UIProgressView with multiple asynchronous NSURLConnection in iOs

I am using following code to download file from url's asynchronously,
NSMutableData *responseData = [[NSMutableData alloc] init];
NSURL *url = [NSURL URLWithString:#"http://www.tuiscos.com/images/trading.png"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
[NSURLConnection sendAsynchronousRequest:request
queue:[NSOperationQueue mainQueue]
completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
// do something with data
[responseData appendData:data];
myImage8.image = [UIImage imageWithData:data];
NSInteger len = response.expectedContentLength;
NSInteger receiverdBytes = 0;
receiverdBytes = data.length+ receiverdBytes;
float prog = (float)[responseData length]/(float)len;
[progress8 setProgress:prog];
}];
as the download progresses, I want to update the progress bar, but using this code, I am not getting a gradual progress, instead it is waiting to complete the download and jumping to the maximum value. How can I make a gradual progress in the value?
Can somebody provide a sample code? For asynchronous method with delegate methods.
Thanks :)
If you don't want to code everything on your own, I would suggest using ASIHTTPRequesst on this task:
http://allseeing-i.com/ASIHTTPRequest/How-to-use
It is very simple to implement and you can do simultaneous, asynchrony downloads. It also provides delegates for all needs, also for progress updates.
I used it in my projects for almost a year now and never regretted it.
CompletionHandler is executed at completion, of course. You have to a delegate for the connection. Use -initWithRequest:delegate: method. You will have to code the NSURLConnectionDelegate methods and the one you need to set progressView value is -connection:didReceiveData:
Here is the doc: https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/URLLoadingSystem/URLLoadingSystem.pdf

NSURLConnection sendSynchronousRequest - background to foreground

I m using sendSynchronousRequest to get the data from the server. I know that synchronous will wait until the data received for that request.
But the problem comes when user by mistake enters some non-existing url and than tries to get response. In this case, if user goes in to background and than comes into foreground it shows only black screen. It only shows status bar. Also its not showing any background application. I have to press Home button to come out of my application.
On simulator, After 1+ minute it shows me the message that "Request time out" (No crash).
On Device, within 1 min application get crashes.
Any suggestion. Any Help. This is really a serious issue in my app.
Thanks.
Just like Julien said, the watchdog is killing your app. To answer some questions:
why does this happen only on the simulator?
Because when you're debugging the watchdog leaves your app alone, it can take time.
why does this happen only when the user enters a wrong url?
Because of the system timeout, the system will keep trying for 60 secs if it can't find a server.
so the problem is synchronous vs asynchronous?
No, the problem is the thread, you can do the same operation in a background thread, just don't do it on the main thread and the watchdog will leave you alone.
why is the screen black when the app comes up?
Remember, you are making blocking stuff on the main thread, the thread that draws...
Hope that was all. Let me know if I missed something.
Why not setting a timeout for your connection?
NSString *urlString = TEST_CONNECTION;
NSError *error = nil;
NSHTTPURLResponse *response = nil;
NSURLRequest *request = [NSURLRequest
requestWithURL:[NSURL URLWithString:urlString]
cachePolicy:NSURLRequestReloadIgnoringCacheData
timeoutInterval:5.0];
NSData *conn = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
This should release the synchronous waiting after a number of seconds, which should solve your problem without going with an asynchronous call (which sometimes isn't the proper solution)
I know this works properly because this is how I check if I am connected to a certain VPN (where reachability flags totally fail).
you should take a look to this article: https://developer.apple.com/library/ios/#qa/qa1693/_index.html
iOs contains a watchdog, if your application is blocked to much time on an operation on the main thread, this one will be killed. (for more details about Watchdog: http://en.wikipedia.org/wiki/Watchdog_timer)
So if you want to download something, don't download it on the main thread.
RELATE
UIImage *image = [self.imgCache objectForKey:urlString];
if(!image){
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:urlString] cachePolicy:NSURLRequestReturnCacheDataElseLoad timeoutInterval:60.0];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSURLResponse *response = nil;
NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:nil];
NSLog(#"%#",response);
UIImage *img = [UIImage imageWithData:data];
//
if(img)
{
dispatch_sync(dispatch_get_main_queue(), ^{
[self.imgCache setObject:img forKey:urlString];
completionBlock(img);
});
}
});
}
else{
completionBlock(image);
}
use ASIHttpRequest class instead of NSURLConnection , its nothing but wrapper around NSURLConnection and has very simple callbacks , you can also set time to complete a request. Please go through this link for more info http://allseeing-i.com/ASIHTTPRequest/
I think you first have to test user data whether it is correct or not and than only if it is correct, sends the request otherwise prompt user that "please enter correct data"...
or
when your parsing of data in response failed. You can also make protocol delegate method i.e FinishWithError so that you come up with your last UI.
Try this one:
#import "ASIHTTPRequest.h"
//In a method
[self performSelectorInBackground:#selector(DownLoadImageInBackground:) withObject:imgUrlArr];
-(void) DownLoadImageInBackground:(NSArray *)imgUrlArr1
{
NSURL * url = [Image URL];
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
[request setDelegate:self];
[request startAsynchronous];
}
-(void)requestFailed:(ASIHTTPRequest *)request
{
NSLog(#"URL Fail : %#",request.url);
NSError *error = [request error];
// you can give here alert too..
}
-(void)requestFinished:(ASIHTTPRequest *)request
{
NSData *responseData = [request responseData];
UIImage *imgInBackground = [[UIImage alloc]
initWithData:responseData];
[imageView setImage: imgInBackground];
}
This might help you: I am also loading a number of images at one time, so images that have no proper data show a black screen. To avoid this, try to resize your imageview.
You could check the reachability of the URL before starting the request.
Apple has Reachability Methods to do so. But its easier to use a wrapper. E.g. ASIReachability.
I think the application crashing because you does not get any data when user enters wrong URL and you are using this 'returned' nil NSData to do stuffs.
I think this will fix your problem
NSData *data=[NSURLConnection sendSynchronousRequest:request
returningResponse:&response
error:&error];
if(data!=nil){
///
} else {
NSLog(#"NO DATA");
}

Loading Image into UIImageView using NSURLConnection

Hey all. I'm really new at this obj-c/xcode stuff. I'm trying to load the background of my xib file. To do this i'm using a UIImageView and populating that with an image I found from the net. the problem with that is that it's REALLY slow. Feels like it's crashing but it's not. I was told to use an NSURLConnection to fix the problem but don't know how. Here's the code i was using previously.
wallpaper.image = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:[NSString stringWithFormat:#"http://mysite.com/mobile/wallpaperR.asp?id=%i",customerID]]]];
How do i translate the above code into the NSURLConnection equivalent?
NSURLConnection will download the data in a new thread, so you app will feel much faster.
It is pretty easy to implement but it does require you to understand the concept of delegation.
I have a useful class I created to handle image downloads on separate threads, I will share the code with you, with comments to let you know what is going on:
AsyncImage.h
AsyncImage.m
If you need help implementing it just leave a comment and I can help you get it working, I remember this used to be pain for me too when I started developing.
You need to do parsing for this as you are using the webservice.Like this
-(void)startParsingForPhotoAlbumList:(NSString*)providerIdString
{
NSString *urlString = [NSString stringWithFormat:#"http://YourUrl/showalbumxml.php?id=%#&show=show",providerIdString];
NSURL *xmlURL = [NSURL URLWithString:urlString];
NSMutableURLRequest *request = [[[NSMutableURLRequest alloc] initWithURL:xmlURL cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:30.0]autorelease];
NSURLResponse *returnedResponse = nil;
NSError *returnedError = nil;
NSData *itemData = [NSURLConnection sendSynchronousRequest:request returningResponse:&returnedResponse error:&returnedError];
self.xmlParser = [[NSXMLParser alloc] initWithData:itemData];
[xmlParser setDelegate:self];
[xmlParser parse];
}
and need to implement parser's delegate method as an example.
- (void)parserDidEndDocument:(NSXMLParser *)parser
{
if ([[[resultArray objectAtIndex:1]objectForKey:#"Transaction"]isEqualToString:#"Myapp/snaps"])
{
[(LoginViewController *)obj getRegisterResult:resultArray];
}
}
Then in your Viewcontroller access the data,from parsing you need to pass objects,using array or dictionary.
NSData *imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:itemImagePath]];
UIImage *image = [[UIImage alloc] initWithData:imageData];
There is an example which may help:
NSURLConnection loading image example

How can I download images without holding up everything else?

I'm making an app that allows you to browse through pictures from a website. I'm currently downloading the images using:
UIImage *myImage = [[UIImage alloc] initWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:url]]];
which works great, but can be time consuming. I start off by downloading 20 images, but I can't do anything until after the 30 or so seconds it takes to download them all.
This one time wait isn't all that bad, but if I want to download the 21st-40th images, I would have to wait another 30 seconds.
Basically, is there a way I can download these images one at a time without holding up any of my animations?
Thanks.
Sure, put the download task in a thread, and use a callback to let your program know when each image is finished. Then you can draw your images as they finish loading, and not hold up the rest of the app. This link has a template that you can use as an example.
Here's a quick and dirty example:
- (void)downloadWorker:(NSString *)urlString
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSURL *url = [NSURL URLWithString:urlString];
NSData *data = [NSData dataWithContentsOfURL:url];
UIImage *image = [[UIImage alloc] initWithData:data];
[self performSelectorOnMainThread:#selector(imageLoaded:)
withObject:image
waitUntilDone:YES];
[image release];
[pool drain];
}
- (void)downloadImageOnThread:(NSString *)url
{
[NSThread detachNewThreadSelector:#selector(downloadWorker:)
toTarget:self
withObject:url];
}
- (void)imageLoaded:(UIImage *)image
{
// get the image into the UI
}
Call downloadImageOnThread for every image you want to load, each will get its own thread, and you'll get calls to imageLoaded as each one completes.
While loading the image on a background thread is definately the solution, I'd use an NSOperation and an NSOperationQueue instead of dealing with the threads yourself (this is the way Apple recommend to deal with threading problems like this!)
The NSOperationQueue will deal with starting/stopping the threads nicely and you can choose how many to run at once etc. It's basically a the same as the other answers but you get a little more control.
There's a tutorial here that looks pretty good.
yeah you can use a secondary thread, and do a lot of work OR you could use things that apple gives us.
NSURLDownload, doesn't "lag" your main thread, You spawn it with a method and you set a endSelector, the endSelector will get called when the download is done.
Spawning a secondary thread for this is not really what you should do.
here you got some code from my app wich does it work perfectly without giving a beach ball of doom.
- (void)downloadAvatar:(NSString *)URL{
NSURL *url = [[NSURL alloc] initWithString:URL];
NSURLRequest *request = [[NSURLRequest alloc] initWithURL:url];
[url release];
NSURLDownload *download = [[NSURLDownload alloc] initWithRequest:request delegate:self];
NSString *path = [[NSString alloc] initWithFormat:#"%#data/%#.jpg",[[BFAppSupport defaultSupport] bfFolderPath],[[xfSession loginIdentity] userName]];
[download setDestination:path allowOverwrite:YES];
[download release];
[path release];
[request release];
}
- (void)downloadDidFinish:(NSURLDownload *)download{
NSString *path = [[NSString alloc] initWithFormat:#"%#data/%#.jpg",[[BFAppSupport defaultSupport] bfFolderPath],[[xfSession loginIdentity] userName]];
NSData *imageData = [[NSData alloc] initWithContentsOfFile:path];
if( [imageData length] < 10 ){
[self performSelector:#selector(downloadAvatar:) withObject:#"http://media.xfire.com/xfire/xf/images/avatars/gallery/default/xfire160.jpg" afterDelay:0.0];
[imageData release];
[path release];
return;
}
NSImage *theImage = [[NSImage alloc] initWithData:imageData];
[imageData release];
[path release];
[yourImage setImage:theImage];
[theImage release];
}
- (void)download:(NSURLDownload *)aDownload didFailWithError:(NSError *)error{
NSLog(#"Avatar url download failed");
}
The code is a bit ugly, but its not hard to change it as you got the 3 things you need, the method that starts the download and 2 that handle or an error or the finish.
You can also use autoreleased objects some more, but in terms of performance I like using it without autoreleased objects when I can.