Recalling viewDidLoad after failed connection not working - iphone

this is my viewDidLoad function working well :
- (void)viewDidLoad
{
[super viewDidLoad];
//iAds code
adview = [[ADBannerView alloc] initWithFrame:CGRectZero];
adview.frame = CGRectOffset(adview.frame, 0, 0);
adview.requiredContentSizeIdentifiers = [NSSet setWithObject:ADBannerContentSizeIdentifierPortrait];
adview.currentContentSizeIdentifier = ADBannerContentSizeIdentifierPortrait;
[self.view addSubview:adview];
adview.delegate = self;
self.adbanerinvesiable = NO;
//php data code
NSError *er;
SBJsonParser *parser = [[SBJsonParser alloc] init];
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:#"http://localhost:8888/newjson.php?number=1"]];
NSData *response = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:nil];
if (response.length != 0){
NSString *json_string = [[NSString alloc]initWithData:response encoding:NSUTF8StringEncoding];
listt = [parser objectWithString:json_string error:&er];
}
else {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Error" message:#"No Internet conniction" delegate:self cancelButtonTitle:#"OK" otherButtonTitles:#"Refresh", nil];
[alert show];
//for ID contet post
SBJsonParser *parser2 = [[SBJsonParser alloc] init];
NSURLRequest *request2 = [NSURLRequest requestWithURL:[NSURL URLWithString:#"http://localhost:8888/newjson.php?number=2"]];
NSData *response2 = [NSURLConnection sendSynchronousRequest:request2 returningResponse:nil error:nil];
NSString *json_string2 = [[NSString alloc]initWithData:response2 encoding:NSUTF8StringEncoding];
IDNum = [parser2 objectWithString:json_string2 error:&er];
}
}
As you can see if the connection failed it will show UIAlertView and two Buttons {ok , Refresh}.
I used this Method in case the refresh button been clicked to call viewDidLoad But not Working and giving me blank white view:
-(void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex{
if (buttonIndex == 1){
[self viewDidLoad];
}
}
Any idea please ?

You should put your PHP Data Code in a method in it's own, so when you need to call that specifically you can, because you can't (or should I say shouldn't) call viewDidLoad manually. That should only be called automatically by UIViewController.
Example
- (void)viewDidLoad
{
[super viewDidLoad];
//iAds code
adview = [[ADBannerView alloc] initWithFrame:CGRectZero];
adview.frame = CGRectOffset(adview.frame, 0, 0);
adview.requiredContentSizeIdentifiers = [NSSet setWithObject:ADBannerContentSizeIdentifierPortrait];
adview.currentContentSizeIdentifier = ADBannerContentSizeIdentifierPortrait;
[self.view addSubview:adview];
adview.delegate = self;
self.adbanerinvesiable = NO;
}
-(void)viewWillAppear:(BOOL)animated /* or: -(void)viewDidAppear: */
{
[super viewWillAppear:animtaed];
[self refreshPHData];
}
-(void)refreshPHPData
{
//php data code
NSError *er;
SBJsonParser *parser = [[SBJsonParser alloc] init];
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:#"http://localhost:8888/newjson.php?number=1"]];
NSData *response = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:nil];
if (response.length != 0){
NSString *json_string = [[NSString alloc]initWithData:response encoding:NSUTF8StringEncoding];
listt = [parser objectWithString:json_string error:&er];
}
else {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Error" message:#"No Internet conniction" delegate:self cancelButtonTitle:#"OK" otherButtonTitles:#"Refresh", nil];
[alert show];
//for ID contet post
SBJsonParser *parser2 = [[SBJsonParser alloc] init];
NSURLRequest *request2 = [NSURLRequest requestWithURL:[NSURL URLWithString:#"http://localhost:8888/newjson.php?number=2"]];
NSData *response2 = [NSURLConnection sendSynchronousRequest:request2 returningResponse:nil error:nil];
NSString *json_string2 = [[NSString alloc]initWithData:response2 encoding:NSUTF8StringEncoding];
IDNum = [parser2 objectWithString:json_string2 error:&er];
}
}
Also, when you call viewDidLoad from your UIAlertView, you are creating a second (and third and so forth) adview because you aren't removing the previous instance. Depending on how many times the alert is shown and viewDidLoad is called, you are going to see adview stacking on top of itself. So try instead:
-(void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex{
if (buttonIndex == 1){
[self refreshPHPData];
}
}

Related

Loading images from parse to image views

I want to load images from parse as you can see in the screenshot.
i am using this code to load the header image into my imageview.
PFQuery *query = [PFQuery queryWithClassName:#"AppOfTheDay"];
[query getObjectInBackgroundWithId:#"WDJpxs4PzH"
block:^(PFObject *retreive, NSError *error) {
{
NSString *text = [retreive objectForKey:#"description"];
if (error) {
[MBProgressHUD hideHUDForView:self.view animated:YES];
UIAlertView *error=[[UIAlertView alloc]initWithTitle:#"Error" message:#"Connection Failed" delegate:self cancelButtonTitle:#"Ok" otherButtonTitles:nil];
[error show];
}
else{
PFFile *imageFile = [retreive objectForKey:#"header"];
[imageFile getDataInBackgroundWithBlock:^(NSData *data, NSError *error) {
if (error) {
[MBProgressHUD hideHUDForView:self.view animated:YES];
UIAlertView *error=[[UIAlertView alloc]initWithTitle:#"Error" message:#"Something went wrong" delegate:self cancelButtonTitle:#"Ok" otherButtonTitles:nil];
[error show];
}
else{
UIImage *image = [UIImage imageWithData:data];
_headerImage.image=image;
_appDescription.text=text;
[MBProgressHUD hideHUDForView:self.view animated:YES];
}
}];
}
}
}];
My question is how can i similarly load my other three images into imageviews?
implement lazy loading images.
for example-NSURL *url = [NSURL URLWithString:urlString];
NSURLRequest *urlRequest = [NSURLRequest requestWithURL:url];
if ( queue == nil )
{
queue = [[NSOperationQueue alloc] init];
}
[NSURLConnection sendAsynchronousRequest:urlRequest queue:queue completionHandler:^(NSURLResponse * resp, NSData *data, NSError *error)
{
dispatch_async(dispatch_get_main_queue(),^
{
if ( error == nil && data )
{
UIImage *urlImage = [[UIImage alloc] initWithData:data];
thumbnail.image = urlImage;
}
});
}];
I hope it will help you alot,It will sort your problem with best loading speed
//yourclass.h
AsyncImageView *imageasy,*imageasy1, *imageasy2,imageasy3;
yourclass.m
PFQuery *query = [PFQuery queryWithClassName:#"AppOfTheDay"];
[query getObjectInBackgroundWithId:#"WDJpxs4PzH"
block:^(PFObject *comment, NSError *error) {
if (!error) {
PFFile *post = [comment objectForKey:#"PhotoName"];
PFFile *post2 = [comment objectForKey:#"logo"];
PFFile *post3 = [comment objectForKey:#"similar1"];
PFFile *post4 = [comment objectForKey:#"similar2"];
imageasy=[[AsyncImageView alloc] init];
imageasy1=[[AsyncImageView alloc] init];
imageasy2=[[AsyncImageView alloc] init];
imageasy3=[[AsyncImageView alloc] init];
[imageasy setImageURL:[NSURL URLWithString:[post url]]];
[imageasy1 setImageURL:[NSURL URLWithString:[post2 url]]];
[imageasy2 setImageURL:[NSURL URLWithString:[post3 url]]];
[imageasy3 setImageURL:[NSURL URLWithString:[post4 url]]];
}];

How to get cookies and use them for other requests like POST ( iOS )?

My previous question was about the problem that I have to login each time for doing web services like posting a link or uploading a picture. Philipe answered that I have to use cookies instead of login process for each request. I found this method for getting cookies:
- (void)getCookies {
NSHTTPURLResponse * response;
NSError * error;
NSMutableURLRequest *request;
request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:#"http://MyWebsite.com/login.php"]
cachePolicy:NSURLRequestReloadIgnoringCacheData
timeoutInterval:120];
NSData * data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
NSLog(#"%#", [[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding]);
NSArray * all = [NSHTTPCookie cookiesWithResponseHeaderFields:[response allHeaderFields] forURL:[NSURL URLWithString:#"http://MyWebsite.com/login.php"]];
NSLog(#"%d", all.count);
for (NSHTTPCookie *cookie in all) {
NSLog(#"Name: %# : Value: %#", cookie.name, cookie.value);
NSLog(#"Comment: %# : CommentURL: %#", cookie.comment, cookie.commentURL);
NSLog(#"Domain: %# : ExpiresDate: %#", cookie.domain, cookie.expiresDate);
NSLog(#"isHTTPOnly: %c : isSecure: %c", cookie.isHTTPOnly, cookie.isSecure);
NSLog(#"isSessionOnly: %c : path: %#", cookie.isSessionOnly, cookie.path);
NSLog(#"portList: %# : properties: %#", cookie.portList, cookie.properties);
NSLog(#"version: %u", cookie.version);
}
}
I also found this code to use these cookies, but I'm not sure how to use it:
[[NSHTTPCookieStorage sharedHTTPCookieStorage] setCookie:cookies];
Here is my method for POSTing, I am using RestKit API:
- (IBAction)addLinkPressed:(UIButton *)sender {
[RKClient clientWithBaseURLString:#"http://MyWebsite.com"];
NSDictionary* params = [NSDictionary dictionaryWithObjectsAndKeys:
self.linkField.text, #"url",
self.linkTitleField.text, #"title",
self.linkSummaryField.text, #"summary",
nil];
RKRequest *request = [[RKClient sharedClient] post:#"/send_link.php" params:params delegate:self];
[request setUserData:#"sendLink"];
}
Question: Which property of cookies should I store to use it for login information and where should I put it in my code?
I solved this issue by some inefficient way. Here is my methodology:
First I try to post to the web service and after posting I parse the returning HTML to see if the posting was successful or not. If posting was successful I give an appropriate message to the user that you post successfully but if it was not successful it could have two reasons: First: there were some error during the post execution Second: the user was not logged in. The way that I recognize the differentiation between fist and second error is just parsing the response HTML.
Here is the code that I used for this methodology (this is for the time that the user wants to change the password)
- (void)objectLoader:(RKObjectLoader*)objectLoader didFailWithError:(NSError*)error {
NSRange range = [[error localizedDescription] rangeOfString:#"-1012"];
if (range.length > 0){
//First error occurs here
}
RKLogError(#"Hit error: %#", error);
}
- (IBAction)requestToChangePasswordPressed {
MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:self.view animated:YES];
hud.labelText = #"Loading";
[RKClient clientWithBaseURLString:#"http://WebServiceDomain.com"];
NSDictionary* params = [NSDictionary dictionaryWithObjectsAndKeys:
self.oldPasswordField.text, #"oldPassword",
self.passwordNew.text, #"newPassword",
self.confirmPasswordField.text, #"confirmPassword",
nil];
RKRequest *request = [[RKClient sharedClient] post:#"/change_password.php" params:params delegate:self];
[request setUserData:#"changePassword"];
[self.view endEditing:YES];
[MBProgressHUD hideHUDForView:self.view animated:YES];
}
- (void)autoLogin {
[RKClient clientWithBaseURLString:#"http://WebServiceDomain.com"];
[RKObjectManager sharedManager].client=[RKClient sharedClient];
RKParams *parameters = [RKParams params];
[parameters setValue:[[NSUserDefaults standardUserDefaults] objectForKey:#"defaultUsername"] forParam:#"username"];
[parameters setValue:[[NSUserDefaults standardUserDefaults] objectForKey:#"defaultPassword"] forParam:#"password"];
[[RKClient sharedClient] setAuthenticationType:RKRequestAuthenticationTypeHTTP];
// because we have two POSTs and we want to use the same method for both of the for didLoadResponse: we set the UserDate like bellow
RKRequest *request = [[RKClient sharedClient] post:#"/login.php" params:parameters delegate:self];
[request setUserData:#"login"];
}
- (void)request:(RKRequest*)request didLoadResponse:(RKResponse*)response
{
MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:self.view animated:YES];
hud.labelText = #"Loading";
id userData = [request userData];
if ([userData isEqual:#"login"]) {
if ([request isGET]) {
// Handling GET /foo.xml
if ([response isOK]) {
// Success! Let's take a look at the data
NSLog(#"Retrieved XML: %#", [response bodyAsString]);
}
} else if ([request isPOST]) {
// Handling POST /other.json
if ([response isJSON]) {
NSLog(#"Got a JSON response back from our POST!");
}
} else if ([request isDELETE]) {
// Handling DELETE /missing_resource.txt
if ([response isNotFound]) {
NSLog(#"The resource path '%#' was not found.", [request resourcePath]);
}
}
}
else if ([userData isEqual:#"sendLink"]) {
NSData *addLinksHtmlData = response.body;
// 2
TFHpple *addlinksParser = [TFHpple hppleWithHTMLData:addLinksHtmlData];
// 3
NSString *errorLinksXpathQueryString = #"//div[#class='errorBox']/ul/li";
NSArray *errorLinksNodes = [addlinksParser searchWithXPathQuery:errorLinksXpathQueryString];
// 4
NSMutableArray *newErrorLinks = [[NSMutableArray alloc] initWithCapacity:0];
for (TFHppleElement *element in errorLinksNodes) {
// 5
AllModels *errorTitle = [[AllModels alloc] init];
[newErrorLinks addObject:errorTitle];
// 6
errorTitle.errorTitle = [[element firstChild] content];
}
// 8
self.linkErrorObjects = newErrorLinks;
NSString *successLinksXpathQueryString = #"//div[#class='successBox']";
NSArray *successLinksNodes = [addlinksParser searchWithXPathQuery:successLinksXpathQueryString];
// 4
NSMutableArray *newSuccessLinks = [[NSMutableArray alloc] initWithCapacity:0];
for (TFHppleElement *element in successLinksNodes) {
// 5
AllModels *successTitle = [[AllModels alloc] init];
[newSuccessLinks addObject:successTitle];
// 6
successTitle.successTitle = [[element firstChild] content];
}
// 8
self.linkSuccessObjects = newSuccessLinks;
}
else {
NSLog(#"HTTP status code: %d", response.statusCode);
NSLog(#"HTTP status message: %#", [response localizedStatusCodeString]);
NSLog(#"Header fields: %#", response.allHeaderFields);
NSLog(#"Body: %#", response.bodyAsString);
NSData *HtmlData = response.body;
// 2
TFHpple *addParser = [TFHpple hppleWithHTMLData:HtmlData];
// 3
NSString *errorXpathQueryString = #"//div[#class='errorBox']/ul/li";
NSArray *errorNodes = [addParser searchWithXPathQuery:errorXpathQueryString];
// 4
NSMutableArray *newError = [[NSMutableArray alloc] initWithCapacity:0];
for (TFHppleElement *element in errorNodes) {
// 5
AllModels *errorTitle = [[AllModels alloc] init];
[newError addObject:errorTitle];
// 6
errorTitle.errorTitle = [[element firstChild] content];
}
// 8
self.ErrorObjects = newError;
NSString *successXpathQueryString = #"//div[#class='successBox']";
NSArray *successNodes = [addParser searchWithXPathQuery:successXpathQueryString];
// 4
NSMutableArray *newSuccess = [[NSMutableArray alloc] initWithCapacity:0];
for (TFHppleElement *element in successNodes) {
// 5
AllModels *successTitle = [[AllModels alloc] init];
[newSuccess addObject:successTitle];
// 6
successTitle.successTitle = [[element firstChild] content];
}
// 8
self.successObjects = newSuccess;
[self errorCheck];
}
[MBProgressHUD hideHUDForView:self.view animated:YES];
[MBProgressHUD hideHUDForView:self.view animated:YES];
}
- (void)errorCheck {
MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:self.view animated:YES];
hud.labelText = #"Loading";
if(self.errorObjects.count > 0) {
AllModels *errorlink = [self.errorObjects objectAtIndex:0];
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"There is a problem" message:errorlink.errorTitle delegate:nil cancelButtonTitle:#"Ok" otherButtonTitles:nil , nil];
[alert show];
}
else {
if(self.linkErrorObjects.count > 0) {
[self autoLogin];
[self requestToChangePasswordPressed];
}
else {
AllModels *successlink = [self.successObjects objectAtIndex:0];
self.successLabel.hidden = NO;
self.successLabel.text = successlink.successTitle;
NSLog(#"Success Title: %#",successlink.successTitle);
[UIView animateWithDuration:3.0
delay:0.0
options:UIViewAnimationOptionBeginFromCurrentState
animations:^{ self.successLabel.alpha = 0.0; }
completion:^(BOOL fin) { if (fin) [self.successLabel removeFromSuperview]; }];
[self performSelector:#selector(dismissModalViewController) withObject:nil afterDelay:1.0];
}
}
[MBProgressHUD hideHUDForView:self.view animated:YES];
[MBProgressHUD hideHUDForView:self.view animated:YES];
}

How can we handle multiple NSURLConnection in iPhone Xcode?

I am developing one small app in which i have multiple NSURLConnection.I have created that NSURL Connection but i don't know how to handle it.My code is like below.
-(void) loadTrafficAndEvent {
int a=10;
//Get the map view bounds for fetch the travel time markers from web service
MKCoordinateRegion region = mapView.region;
float print = region.center.latitude;
// NSLog(#"region.center=%g",print);
CGPoint firstcorner = CGPointMake(self.mapView.bounds.origin.x , mapView.bounds.origin.y);
CGPoint secondcorner = CGPointMake((self.mapView.bounds.origin.x+mapView.bounds.size.width) , mapView.bounds.origin.y);
CGPoint thirdcorner = CGPointMake(self.mapView.bounds.origin.x , (mapView.bounds.origin.y+ mapView.bounds.size.height));
CGPoint fourthcorner = CGPointMake((self.mapView.bounds.origin.x+mapView.bounds.size.width), (mapView.bounds.origin.y + mapView.bounds.size.height));;
//Then transform those point into lat,lng values
CLLocationCoordinate2D mapfirstcorner,mapsecondcorner,mapthirdcorner,mapfourthcorner,requestsender;
mapfirstcorner = [mapView convertPoint:firstcorner toCoordinateFromView:mapView];
mapsecondcorner = [mapView convertPoint:secondcorner toCoordinateFromView:mapView];
mapthirdcorner = [mapView convertPoint:thirdcorner toCoordinateFromView:mapView];
mapfourthcorner = [mapView convertPoint:fourthcorner toCoordinateFromView:mapView];
NSDateFormatter *dateFormatter;
dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"MM-dd-yyyy"];
NSString *date = [dateFormatter stringFromDate:[NSDate date]];
[dateFormatter release];
NSString *checksumString = [NSString stringWithFormat:#"TrafficAndEvents%#ForTravelStar", date];
NSString *md5Checksum = [self getMD5CheckSum:checksumString];
NSString *url = [NSString stringWithFormat:#"http://www.travelstar.nl/travelstarwebservice/ProviderServices.asmx/GetTrafficStatusAndEvent?northWestLatitude=%f&northWestLongitude=%f&southEastLatitude=%f&southEastLongitude=%f&zoomLevel=%d&date=%#&checksum=%#", mapfirstcorner.latitude,mapfirstcorner.longitude, self.mapView.region.center.latitude, self.mapView.region.center.longitude, a,date,md5Checksum];
NSString *url1 = [NSString stringWithFormat:#"http://www.travelstar.nl/travelstarwebservice/ProviderServices.asmx/GetTrafficStatusAndEvent?northWestLatitude=%f&northWestLongitude=%f&southEastLatitude=%f&southEastLongitude=%f&zoomLevel=%d&date=%#&checksum=%#", mapsecondcorner.latitude,mapsecondcorner.longitude, self.mapView.region.center.latitude, self.mapView.region.center.longitude, a,date,md5Checksum];
NSString *url2 = [NSString stringWithFormat:#"http://www.travelstar.nl/travelstarwebservice/ProviderServices.asmx/GetTrafficStatusAndEvent?northWestLatitude=%f&northWestLongitude=%f&southEastLatitude=%f&southEastLongitude=%f&zoomLevel=%d&date=%#&checksum=%#", mapthirdcorner.latitude,mapthirdcorner.longitude, self.mapView.region.center.latitude, self.mapView.region.center.longitude, a,date,md5Checksum];
NSString *url3 = [NSString stringWithFormat:#"http://www.travelstar.nl/travelstarwebservice/ProviderServices.asmx/GetTrafficStatusAndEvent?northWestLatitude=%f&northWestLongitude=%f&southEastLatitude=%f&southEastLongitude=%f&zoomLevel=%d&date=%#&checksum=%#", mapfourthcorner.latitude,mapfourthcorner.longitude, self.mapView.region.center.latitude, self.mapView.region.center.longitude, a,date,md5Checksum];
//Release the request if it is already created.
if(request1 ) {
[request release];
request = nil;
}
else if(request1 ) {
[request1 release];
request1 = nil;
}
else if(request2 ) {
[request2 release];
request2 = nil;
}
else if(request3 ) {
[request3 release];
request3 = nil;
}
//Release the connection if it is already created.
if(conn) {
[conn cancel];
[conn release];
conn = nil;
}
else if(conn1) {
[conn1 cancel];
[conn1 release];
conn1 = nil;
}
else if(conn2) {
[conn2 cancel];
[conn2 release];
conn2 = nil;
}
else if(conn3) {
[conn3 cancel];
[conn3 release];
conn3 = nil;
}
//If zoom level is grater then 6 then it will request for fetch the travel time markers from the web servce.
if(a > 6) {
ZOOM_LEVEL = a;
//Create the request for fetch the data from web service.
request = [[NSURLRequest alloc] initWithURL:[NSURL URLWithString:url]];
request1 = [[NSURLRequest alloc] initWithURL:[NSURL URLWithString:url1]];
request2 = [[NSURLRequest alloc] initWithURL:[NSURL URLWithString:url2]];
request3 = [[NSURLRequest alloc] initWithURL:[NSURL URLWithString:url3]];
//NSLog(#"%#",url);
//NSLog(#"Request sent");
//entryDate = [NSDate date];
//[entryDate retain];
//Create the connection with the web service for fetch the data
// DownloadDelegate *dd = [[DownloadDelegate alloc] init];
conn = [[NSURLConnection alloc] initWithRequest:request delegate:self];
conn1 = [[NSURLConnection alloc] initWithRequest:request1 delegate:self];
conn2 = [[NSURLConnection alloc] initWithRequest:request2 delegate:self];
conn3 = [[NSURLConnection alloc] initWithRequest:request3 delegate:self];
}
}
Declare conn,conn1,conn2,conn3 in .h file.
Then do the following.
in loadTrafficAndEvent:
conn1 = [[NSURLConnection alloc] initWithRequest:request1 delegate:self];
in connectionDidFinishDownloading: method,
- (void)connectionDidFinishDownloading:(NSURLConnection *)connection destinationURL:(NSURL *)destinationURL{
if(connection==conn){
conn1 = [[NSURLConnection alloc] initWithRequest:request1 delegate:self];
}
else if(connection==conn1){
conn2 = [[NSURLConnection alloc] initWithRequest:request2 delegate:self];
}
else if(connection==conn2){
conn3 = [[NSURLConnection alloc] initWithRequest:request3 delegate:self];
}
}
Do the operations inside each if else condition, and no need to allocate and initialize all NSURLConnection in loadTrafficAndEvent: The download will occur one after other.
you might want to take a look at AFNetworking for an easier and tidier way of doing network requests.
Implement NSURLConnection delegate methods and rest will be handle by itself

NSOperation and EXC_BAD_ACCESS

I have a few apps which are largely data driven, so most screens are basically composed of:
Open the screen
Download the data via an NSOperation
Display data in a UITableView
Make a selection from the UITableView
Go to new screen, and start over from step 1
I am finding that everything works in normal usage, but if the user goes away from the app for a while and then comes back, I'm getting an EXC_BAD_ACCESS error when the next NSOperation runs. This doesn't seem to matter if the user sends the app into the background or not, and it only seems to occur if there's been at least a few mins since the previous data connection was made.
I realise this must be some form of over-releasing, but I'm pretty good with my memory management and I can't see anything wrong. My data calls generally look like this:
-(void)viewDidLoad {
[super viewDidLoad];
NSOperationQueue* tmpQueue = [[NSOperationQueue alloc] init];
self.queue = tmpQueue;
[tmpQueue release];
}
-(void)loadHistory {
GetHistoryOperation* operation = [[GetHistoryOperation alloc] init];
[operation addObserver:self forKeyPath:#"isFinished" options:0 context:NULL];
[self.queue addOperation:operation];
[operation release];
}
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
if ([keyPath isEqual:#"isFinished"] && [object isKindOfClass:[GetHistoryOperation class]]) {
GetHistoryOperation* operation = (GetHistoryOperation*)object;
if(operation.success) {
[self performSelectorOnMainThread:#selector(loadHistorySuceeded:) withObject:operation waitUntilDone:YES];
} else {
[self performSelectorOnMainThread:#selector(loadHistoryFailed:) withObject:operation waitUntilDone:YES];
}
} else {
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
}
}
-(void)loadHistorySuceeded:(GetHistoryOperation*)operation {
if([operation.historyItems count] > 0) {
//display data here
} else {
//display no data alert
}
}
-(void)loadHistoryFailed:(GetHistoryOperation*)operation {
//show failure alert
}
And my operations generally looks something like this:
-(void)main {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSError* error = nil;
NSString* postData = [self postData];
NSDictionary *dictionary = [RequestHelper performPostRequest:kGetUserWalkHistoryUrl:postData:&error];
if(dictionary) {
NSNumber* isValid = [dictionary objectForKey:#"IsValid"];
if([isValid boolValue]) {
NSMutableArray* tmpDays = [[NSMutableArray alloc] init];
NSMutableDictionary* tmpWalksDictionary = [[NSMutableDictionary alloc] init];
NSDateFormatter* dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"yyyyMMdd"];
NSArray* walksArray = [dictionary objectForKey:#"WalkHistories"];
for(NSDictionary* walkDictionary in walksArray) {
Walk* walk = [[Walk alloc] init];
walk.name = [walkDictionary objectForKey:#"WalkName"];
NSNumber* seconds = [walkDictionary objectForKey:#"TimeTaken"];
walk.seconds = [seconds longLongValue];
NSString* dateStart = [walkDictionary objectForKey:#"DateStart"];
NSString* dateEnd = [walkDictionary objectForKey:#"DateEnd"];
walk.startDate = [JSONHelper convertJSONDate:dateStart];
walk.endDate = [JSONHelper convertJSONDate:dateEnd];
NSString* dayKey = [dateFormatter stringFromDate:walk.startDate];
NSMutableArray* dayWalks = [tmpWalksDictionary objectForKey:dayKey];
if(!dayWalks) {
[tmpDays addObject:dayKey];
NSMutableArray* dayArray = [[NSMutableArray alloc] init];
[tmpWalksDictionary setObject:dayArray forKey:dayKey];
[dayArray release];
dayWalks = [tmpWalksDictionary objectForKey:dayKey];
}
[dayWalks addObject:walk];
[walk release];
}
for(NSString* dayKey in tmpDays) {
NSMutableArray* dayArray = [tmpWalksDictionary objectForKey:dayKey];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"startDate" ascending:YES];
NSArray *sortDescriptors = [NSArray arrayWithObject:sortDescriptor];
NSArray* sortedDayArray = [dayArray sortedArrayUsingDescriptors:sortDescriptors];
[sortDescriptor release];
[tmpWalksDictionary setObject:sortedDayArray forKey:dayKey];
}
NSSortDescriptor* sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:nil ascending:NO selector:#selector(localizedCompare:)];
self.days = [tmpDays sortedArrayUsingDescriptors:[NSArray arrayWithObject:sortDescriptor]];
self.walks = [NSDictionary dictionaryWithDictionary:tmpWalksDictionary];
[tmpDays release];
[tmpWalksDictionary release];
[dateFormatter release];
self.success = YES;
} else {
self.success = NO;
self.errorString = [dictionary objectForKey:#"Error"];
}
if([dictionary objectForKey:#"Key"]) {
self.key = [dictionary objectForKey:#"Key"];
}
} else {
self.errorString = [error localizedDescription];
if(!self.errorString) {
self.errorString = #"Unknown Error";
}
self.success = NO;
}
[pool release];
}
-(NSString*)postData {
NSMutableString* postData = [[[NSMutableString alloc] init] autorelease];
[postData appendFormat:#"%#=%#", #"LoginKey", self.key];
return [NSString stringWithString:postData];
}
----
#implementation RequestHelper
+(NSDictionary*)performPostRequest:(NSString*)urlString:(NSString*)postData:(NSError**)error {
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
NSURL* url = [NSURL URLWithString:[NSString stringWithFormat:#"%#/%#", kHostName, urlString]];
NSMutableURLRequest *urlRequest = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:30];
[urlRequest setHTTPMethod:#"POST"];
if(postData && ![postData isEqualToString:#""]) {
NSString *postLength = [NSString stringWithFormat:#"%d", [postData length]];
[urlRequest setHTTPBody:[postData dataUsingEncoding:NSASCIIStringEncoding]];
[urlRequest setValue:postLength forHTTPHeaderField:#"Content-Length"];
[urlRequest setValue:#"application/x-www-form-urlencoded" forHTTPHeaderField:#"Content-Type"];
}
NSURLResponse *response = nil;
error = nil;
NSData *jsonData = [NSURLConnection sendSynchronousRequest:(NSURLRequest *)urlRequest returningResponse:(NSURLResponse **)&response error:(NSError **)&error];
NSString *jsonString = [[NSString alloc] initWithBytes: [jsonData bytes] length:[jsonData length] encoding:NSUTF8StringEncoding];
NSLog(#"JSON: %#",jsonString);
//parse JSON
NSDictionary *dictionary = nil;
if([jsonData length] > 0) {
dictionary = [[CJSONDeserializer deserializer] deserializeAsDictionary:jsonData error:error];
}
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
return dictionary;
}
If I have the autorelease pool in place, the crash occurs on [pool release]. If I don't, then the crash just looks to appear in the main.m method, and I don't seem to get any useful information. It's difficult to track down when I have to wait 10 mins in between every test!
If anyone can offer any clues or directions to go, that'd be much appreciated.
It's almost certain you're overreleasing something in your code, seeing that the crash is occurring during a [pool release] (There's a autorelease pool in the main method as well).
You can find it using Xcode - use build and analyze to have the static analyser pinpoint potential problems. Run it and post the results.
try this:
http://cocoadev.com/index.pl?NSZombieEnabled
also, you should avoid:
1) calling UIKit methods from secondary threads
2) making (synchronous) url requests from the main thread.
you must be doing one in any case in RequestHelper's performPostRequest method.
My guess is this section
GetHistoryOperation* operation = (GetHistoryOperation*)object;
if(operation.success) {
[self performSelectorOnMainThread:#selector(loadHistorySuceeded:) withObject:operation waitUntilDone:YES];
} else {
[self performSelectorOnMainThread:#selector(loadHistoryFailed:) withObject:operation waitUntilDone:YES];
}
If the sleep happens at a bad point here, you have an object being passed to another thread. I'd find a way around having to pass the operation as the object.
This is a really old question, so sorry for the dredge, but there is no accepted answer.
I was also getting a EXC_BAD_ACCESS on NSOperationQueue -addOperation for seemingly no reason, and after a few days of hunting down memory leaks, and turning on all the debugger options i could find (malloc guard, zombies) and getting nothing, I found an NSLog warning that said: "[NSoperation subclass] set to IsFinished before being started by the queue."
When I modified my base operation subclass, so that its -cancel function only set (IsRunning = NO) and (IsFinished = YES) IF AND ONLY IF (IsRunning == YES), NSOperationQueue stopped crashing.
So if you're ever calling NSOperationQueue -cancelAllOperations, or you're doing that manually (i.e. for (NSOperation *op in queue.allOperations) ) double check to make sure that you don't set IsFinished on those operations in your subclass implementation.

How to show/hide UIImageView when I want

I'm having a little problem with an iPhone app I'm currently developing.
When the user touch a button, it calls an IBAction named refreshQuestion, which is supposed to show an image hover the screen to ask the user to wait a moment, then it has to call another function, and finally it has to hide the image.
The problem is that the image won't appear. As well as the network activity indicator.
Any help?
Here is the code :
- (IBAction)refreshQuestion:(id)sender{
pleaseWait.hidden = NO;
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
[self loadData];
pleaseWait.hidden = YES;
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
}
EDIT :
Here is my LoadData function :
- (void)loadData{
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
NSString * idUtilisateur = [userDefaults objectForKey:#"idUtilisateur"];
NSString * stringUrlQuestion = [NSString stringWithFormat:#"http://www.mywebsiteURL"];
NSURL * urlQuestion = [NSURL URLWithString:stringUrlQuestion];
QuestionParser * parser = [[QuestionParser alloc] init];
[parser parseXMLAtURL:urlQuestion parseError:nil] ;
int nbQuestions = [parser.arrayOfQuestion count];
[parser release];
NSFetchRequest *requete = [[NSFetchRequest alloc] init];
NSEntityDescription *entite = [NSEntityDescription entityForName:#"Question" inManagedObjectContext:self.managedObjectContext];
[requete setEntity:entite];
NSSortDescriptor * sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"idQuestion" ascending:YES];
NSArray * sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
[requete setSortDescriptors:sortDescriptors];
[sortDescriptor release];
[sortDescriptors release];
NSError * error;
NSMutableArray *mutableFetchResult = [[self.managedObjectContext executeFetchRequest:requete error:&error] mutableCopy];
[requete release];
if(mutableFetchResult == nil){
NSLog(#"Erreur viewWillAppear : %#", error);
}
questionDuJour = [mutableFetchResult objectAtIndex:0];
if (nbQuestions == 0){
UIAlertView* alertViewConnection = [[UIAlertView alloc] initWithTitle:#"Error" message:#"Error while retreiving data" delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alertViewConnection show];
[alertViewConnection release];
}
NSDateFormatter *outputFormatter = [[NSDateFormatter alloc] init];
[outputFormatter setDateFormat:#"EEEE d MMMM"];
NSLocale * localisation = [[[NSLocale alloc] initWithLocaleIdentifier:#"fr_FR"] autorelease];
[outputFormatter setLocale:localisation];
labelJour.text = [NSString stringWithFormat:#"%#",[outputFormatter stringFromDate:questionDuJour.dateQuestion]];
textQuestion.text = questionDuJour.textQuestion;
citation.text = [NSString stringWithFormat:#"%#",questionDuJour.citation];
labelAuteur.text = [NSString stringWithFormat:#"%#",questionDuJour.auteur];
[outputFormatter release];
NSLog(#"stop animating");
}
I think your code [self loadData] executes in microseconds.
Check by using this code alone,
pleaseWait.hidden = NO;
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
EDIT:
Check this, if you still get the same problem check whether you have assigned IBoutlet or not. Because what you are doing is the correct way.
Sounds like everything is executing on the UI Thread. Post your loadData method so we can see what its doing. Chances are you have to convert this method to use one of the asynchronous techniques which will return immediately and allow the UI thread to continue. You can then detect when its done loading the data and change the visibility of the image again.