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.
Related
I'm having trouble on Core Data, been driving me crazy for over a day now. When I quit my app using Xcode I lose all Core Data relationships. All of the objects are still in the SQL database, but the relationships between them are gone. I looked at this which was the closest thing:
Some CoreData relationships are lost after closing app
I tried using mutableSetValueForKeyPath:
if ([[NSString stringWithFormat:#"%#",[item mutableSetValueForKeyPath:#"clothing_type"]] isEqualToString:#"Top"])
return item;
But I just get:
'NSInvalidArgumentException', reason: 'NSManagedObjects of entity 'ClothingItem' do not support -mutableSetValueForKey: for the property 'clothing_type''
Could someone help with this? The below code is where the main problem is happening. I set up the wardrobe and everything is fine. getTop, getBottom and getShoes work fine between views, etc. Even when the app goes to background and returns it's all fine. But I do the quitting of the app via xcode and when I come back I run the debugger and the wardrobe is there, but when I run getTop, getBottom or getShoes on it it returns nil because #dynamic clothes is nil. Here's the wardrobe file:
#import "Wardrobe.h"
#import "ClothingItem.h"
#implementation Wardrobe
#dynamic clothes;
- (ClothingItem*) getTop
{
for (ClothingItem *item in clothes){
if ([[item valueForKey:#"clothing_type"] isEqualToString:#"Top"])
return item;
}
return nil;
}
- (ClothingItem*) getBottom
{
for (ClothingItem *item in clothes){
if ([[item valueForKey:#"clothing_type"] isEqualToString:#"Bottom"])
return item;
}
return nil;
}
- (ClothingItem*) getShoes
{
for (ClothingItem *item in clothes){
if ([[item valueForKey:#"clothing_type"] isEqualToString:#"Shoes"])
return item;
}
return nil;
}
#end
Here is where I set up the favorite. I was initially saving it as a multidimensional NSMutableArray and storing it in NSUserDefaults but again when I returned it was gone. I guess a multidimensional NSMutableArray is not able to be stored that way. So I decided to go with Core Data which seems to be the most proper thing to do:
- (IBAction)saveCurrentAsFavorite:(id)sender {
AppDelegate *delegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
NSManagedObject *top = nil;
NSManagedObject *bottom = nil;
NSManagedObject *shoes = nil;
UIAlertView *myAlert;
if (delegate.topClothesList.count > delegate.currentTopNumber){
top = [delegate.topClothesList objectAtIndex:delegate.currentTopNumber];
}
else {
UIAlertView *myAlert = [[UIAlertView alloc] initWithTitle: #"No Top!" message: #"Please select a top if you want to add to favorites!" delegate: nil cancelButtonTitle:#"Okie dokie!" otherButtonTitles:nil];
[myAlert show];
return;
}
if (delegate.bottomClothesList.count > delegate.currentBottomNumber){
bottom = [delegate.bottomClothesList objectAtIndex:delegate.currentBottomNumber];
}
else {
UIAlertView *myAlert = [[UIAlertView alloc] initWithTitle: #"No Bottom!" message: #"Please select a bottom if you want to add to favorites!" delegate: nil cancelButtonTitle:#"Roger!" otherButtonTitles:nil];
[myAlert show];
return;
}
if (delegate.shoesClothesList.count > delegate.currentShoeNumber){
shoes = [delegate.shoesClothesList objectAtIndex:delegate.currentShoeNumber];
}
else {
//shoes = nil;
UIAlertView *myAlert = [[UIAlertView alloc] initWithTitle: #"No Shoes!" message: #"Please select shoes if you want to add to favorites!" delegate: nil cancelButtonTitle:#"Will do!" otherButtonTitles:nil];
[myAlert show];
return;
}
// The old way of doing it where I saved it all in an NSMutableArray, but it wouldn't save into NSUserDefaults as a multidimensional NSMutableArray…
/* NSMutableArray *wardrobe = [[NSMutableArray alloc] initWithObjects:top, bottom, shoes, nil];
for (int i = 0; i < delegate.favoriteWardrobes.count; i++){
if ([[[delegate.favoriteWardrobes objectAtIndex:i] objectAtIndex:0] isEqual: [wardrobe objectAtIndex:0]] && [[[delegate.favoriteWardrobes objectAtIndex:i] objectAtIndex:1] isEqual: [wardrobe objectAtIndex:1]] && [[[delegate.favoriteWardrobes objectAtIndex:i] objectAtIndex:2] isEqual: [wardrobe objectAtIndex:2]]){
myAlert = [[UIAlertView alloc] initWithTitle: #"Wardrobe Already In Favorites!" message: #"Very nice choice, again! You like this wardrobe, so you should just buy it! Click the images so you could visit their sites and buy them!" delegate: nil cancelButtonTitle:#"Nice!" otherButtonTitles:nil];
[myAlert show];
return;
}
}
[delegate.favoriteWardrobes addObject:wardrobe];
// save information
NSArray *values = [[NSArray alloc] initWithObjects:delegate.selectedTopStyles, delegate.selectedBottomStyles, delegate.selectedShoesStyles, delegate.selectedSexes, delegate.selectedPrices, delegate.favoriteWardrobes, nil];
[values writeToFile:[self saveFilePath] atomically:YES];
//[[NSUserDefaults standardUserDefaults] setObject:delegate.favoriteWardrobes forKey:#"favoriteWardrobes"];
myAlert = [[UIAlertView alloc] initWithTitle: #"Wardrobe Added To Favorites!" message: #"Very nice choice! Awesome style! \n\nReminder: if you select View Favorities you can send this wardrobe to yourself and your friends!" delegate: nil cancelButtonTitle:#"Sweet!" otherButtonTitles:nil];
[myAlert show];
*/
// TRY DOING IT THE CORE DATA WAY
// need to fetch the Wardrobe objects and check their children to see if they match; and if any of these children do not exist within that wardrobe, skip it. If there is a wardrobe that contains all three then say wardrobe is already added
NSEntityDescription *entitydesc = [NSEntityDescription entityForName:#"Wardrobe" inManagedObjectContext:context];
NSFetchRequest *request = [[NSFetchRequest alloc]init];
[request setEntity:entitydesc];
[request setIncludesSubentities:NO];
NSError *error = nil;
NSArray *fetchedObjects = [context executeFetchRequest:request error:&error];
ClothingItem *clothingTop, *clothingBottom, *clothingShoes;
for (Wardrobe *ward in fetchedObjects) {
clothingTop = [ward getTop];
clothingBottom = [ward getBottom];
clothingShoes = [ward getShoes];
if (([[clothingTop valueForKey:#"clothing_id"] isEqual:[top valueForKey:#"clothing_id"]]) && ([[clothingBottom valueForKey:#"clothing_id"] isEqual:[bottom valueForKey:#"clothing_id"]]) && ([[clothingShoes valueForKey:#"clothing_id"] isEqual:[shoes valueForKey:#"clothing_id"]])){
myAlert = [[UIAlertView alloc] initWithTitle: #"Wardrobe Already In Favorites!" message: #"Very nice choice, again! You like this wardrobe, so you should just buy it! Click the images so you could visit their sites and buy them!" delegate: nil cancelButtonTitle:#"Nice!" otherButtonTitles:nil];
[myAlert show];
return;
}
}
// if there's no case like that and this wardrobe is unique then do the following:
// Grab the Wardrobe entity
Wardrobe *newWardrobe = [NSEntityDescription insertNewObjectForEntityForName:#"Wardrobe" inManagedObjectContext:context];
/*ClothingItem* topCloth = (ClothingItem *) top;
ClothingItem* bottomCloth = (ClothingItem *) bottom;
ClothingItem* shoesCloth = (ClothingItem *) shoes;*/
// Instead of doing straight casting because that doesn't seem recommended, do more fetch requests to be extra careful
// We're looking to grab ClothingItems
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"ClothingItem"
inManagedObjectContext:context];
[fetchRequest setEntity:entity];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"clothing_id == %#", [top valueForKey:#"clothing_id"]];
[fetchRequest setPredicate:predicate];
error = nil;
fetchedObjects = [context executeFetchRequest:fetchRequest error:&error];
ClothingItem* topCloth = [fetchedObjects objectAtIndex:0];
predicate = [NSPredicate predicateWithFormat:#"clothing_id == %#", [bottom valueForKey:#"clothing_id"]];
[fetchRequest setPredicate:predicate];
error = nil;
fetchedObjects = [context executeFetchRequest:fetchRequest error:&error];
ClothingItem* bottomCloth = [fetchedObjects objectAtIndex:0];
predicate = [NSPredicate predicateWithFormat:#"clothing_id == %#", [shoes valueForKey:#"clothing_id"]];
[fetchRequest setPredicate:predicate];
error = nil;
fetchedObjects = [context executeFetchRequest:fetchRequest error:&error];
ClothingItem* shoesCloth = [fetchedObjects objectAtIndex:0];
// adding wardrobe to the clothes
[topCloth addWardrobeObject:newWardrobe];
[bottomCloth addWardrobeObject:newWardrobe];
[shoesCloth addWardrobeObject:newWardrobe];
// adding clothes to wardrobe
// adding individually just to see if it works this way; the NSSet below was working fine
[newWardrobe addClothesObject:topCloth];
[newWardrobe addClothesObject:bottomCloth];
[newWardrobe addClothesObject:shoesCloth];
//[newWardrobe addClothes:[NSSet setWithObjects:topCloth, bottomCloth, shoesCloth, nil]];
if ([context save:&error]) {
NSLog(#"The save was successful!");
} else {
NSLog(#"The save wasn't successful: %#", [error localizedDescription]);
}
//[delegate saveContext]; // does this help?
//[newWardrobe addClothesObject:[delegate.topClothesList objectAtIndex:delegate.currentTopNumber]];
//[newWardrobe addClothesObject:[delegate.bottomClothesList objectAtIndex:delegate.currentBottomNumber]];
//[newWardrobe addClothesObject:[delegate.shoesClothesList objectAtIndex:delegate.currentShoeNumber]];
// not sure if this is going to work!
//[newWardrobe addClothesObject:(ClothingItem*) top];
//[newWardrobe addClothesObject:(ClothingItem*) bottom];
//[newWardrobe addClothesObject:(ClothingItem*) shoes];
delegate.numFavorites++; // add another favorite
[[NSUserDefaults standardUserDefaults] setInteger:delegate.numFavorites forKey:#"numFavorites"];
myAlert = [[UIAlertView alloc] initWithTitle: #"Wardrobe Added To Favorites!" message: #"Very nice choice! Awesome style! \n\nReminder: if you select View Favorities you can send this wardrobe to yourself and your friends!" delegate: nil cancelButtonTitle:#"Sweet!" otherButtonTitles:nil];
[myAlert show];
}
Here's the code where I'm calling getTop, getBottom, getShoes:
// Construct a fetch request
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Wardrobe"
inManagedObjectContext:context];
[request setIncludesSubentities:NO];
[request setEntity:entity];
NSError *error = nil;
NSArray *fetchedObjects = [context executeFetchRequest:request error:&error];
//ClothingItem *clothingTop, *clothingBottom, *clothingShoes;
NSManagedObject *top = [[fetchedObjects objectAtIndex:currentWardobe] getTop];//[[delegate.favoriteWardrobes objectAtIndex:currentWardobe] objectAtIndex:0];
NSManagedObject *bottom = [[fetchedObjects objectAtIndex:currentWardobe] getBottom];//[[delegate.favoriteWardrobes objectAtIndex:currentWardobe] objectAtIndex:1];
NSManagedObject *shoes = [[fetchedObjects objectAtIndex:currentWardobe] getShoes];//[[delegate.favoriteWardrobes objectAtIndex:currentWardobe] objectAtIndex:2];
Any ideas on what might be going wrong here?
In the below sample code, I am a bit lost as to why I am getting a NZombie on the line:
[Category getInitialDataToDisplay:[self getDBPath]];
I have looked through SO posts and other documentation but am new to objective-c and am spinning my wheels on this.
- (void)applicationDidFinishLaunching:(UIApplication *)application {
//Copy database to the user's phone if needed.
[self copyDatabaseIfNeeded];
// Init the Array
activeCategories = [[NSMutableArray alloc] init];
activeSubjects = [[NSMutableArray alloc] init];
categories = [[NSMutableArray alloc] init];
subjects = [[NSMutableArray alloc] init];
quotes = [[NSMutableArray alloc] init];
quoteMaps = [[NSMutableArray alloc] init];
//Initialize the Category array.
NSMutableArray *tempArray = [[NSMutableArray alloc] init];
self.categories = tempArray;
[tempArray release];
//Once the db is copied, get the initial data to display on the screen.
[Category getInitialDataToDisplay:[self getDBPath]];
//populate active subjects and categories:
activeCategories = [self getActiveCategories];
activeSubjects = [self getActiveSubjects];
// sort data
NSSortDescriptor *categorySorter;
NSSortDescriptor *subjectSorter;
categorySorter = [[NSSortDescriptor alloc]initWithKey:#"category_title" ascending:YES];
subjectSorter = [[NSSortDescriptor alloc]initWithKey:#"title" ascending:YES];
NSArray *sortDescriptorsCat = [NSArray arrayWithObject:categorySorter];
NSArray *sortDescriptorsSub = [NSArray arrayWithObject:subjectSorter];
[self.categories sortUsingDescriptors:sortDescriptorsCat];
[self.subjects sortUsingDescriptors:sortDescriptorsSub];
[categorySorter release];
[subjectSorter release];
// Configure and show the window
[window addSubview:[navigationController view]];
[window makeKeyAndVisible];
}
...
- (void)dealloc {
[activeSubjects release];
[activeCategories release];
[categories autorelease];
[subjects autorelease];
[quotes autorelease];
[quoteMaps autorelease];
[navigationController release];
[window release];
[super dealloc];
}
Here is the getInitialDataToDisplay:
+ (void) getInitialDataToDisplay:(NSString *)dbPath {
// Use this section to bring in database and populate the array
FMDatabase *database = [FMDatabase databaseWithPath:dbPath];
[database open];
QuotesAppDelegate *appDelegate = (QuotesAppDelegate *)[[UIApplication sharedApplication] delegate];
//appDelegate.categories = [appDelegate.categories sortedArrayUsingSelector:#selector(caseInsensitiveCompare:)];
//POPULATE THE SUBJECT
FMResultSet *result_subjects = [database executeQuery:#"select * from SUBJECT"];
while([result_subjects next]) {
NSInteger primaryKey = [result_subjects intForColumn:#"SUBJECT_ID"];
Subject *sub = [[Subject alloc] initWithPrimaryKey:primaryKey];
sub.title = [result_subjects stringForColumn:#"SUBJECT"];
sub.category_title = [result_subjects stringForColumn:#"CATEGORY"];
sub.active = [result_subjects intForColumn:#"ACTIVE"];
sub.isDirty = NO;
[appDelegate.subjects addObject:sub];
[sub release];
}
FMResultSet *result_categories = [database executeQuery:#"select distinct category from SUBJECT"];
while([result_categories next]) {
Category *cat = [[Category alloc] init];
cat.category_title = [result_categories stringForColumn:#"CATEGORY"];
NSLog(#"loading category: %#", cat.category_title);
QuotesAppDelegate *appDelegate = (QuotesAppDelegate *)[[UIApplication sharedApplication] delegate];
for (Subject *sb in appDelegate.subjects){
if([cat.category_title isEqualToString:sb.category_title]){
[cat.subjects addObject:sb];
NSLog(#" Adding subject: %# cat.subjects.count=%i", sb.title, cat.subjects.count);
}
}
[appDelegate.categories addObject:cat];
[cat release];
}
//POPULATE THE QUOTES
FMResultSet *result_quotes = [database executeQuery:#"select * from QUOTE"];
while([result_quotes next]) {
Quote *sub = [Quote alloc];
sub.quote_id = [result_quotes stringForColumn:#"QUOTE_ID"];
sub.quote_date = [result_quotes stringForColumn:#"DATE"];
sub.title = [result_quotes stringForColumn:#"DESC1"];
sub.desc2 = [result_quotes stringForColumn:#"DESC2"];
sub.excerpt = [result_quotes stringForColumn:#"EXCERPT"];
sub.note = [result_quotes stringForColumn:#"NOTES"];
sub.isDirty = NO;
[appDelegate.quotes addObject:sub];
[sub release];
}
//POPULATE THE QUOTE_MAPS
FMResultSet *result_quote_map = [database executeQuery:#"select * from QUOTE_MAP"];
while([result_quote_map next]) {
QuoteMap *sub = [QuoteMap alloc];
sub.quote_id = [result_quote_map stringForColumn:#"QUOTE_ID"];
sub.quote_map_id = [result_quote_map stringForColumn:#"QUOTE_MAP_ID"];
sub.subject_id = [result_quote_map stringForColumn:#"SUBJECT_ID"];
sub.isDirty = NO;
[appDelegate.quoteMaps addObject:sub];
[sub release];
}
[database close];
NSLog(#"Count of categories: %i", appDelegate.categories.count);
NSLog(#"Count of subjects: %i", appDelegate.subjects.count);
NSLog(#"Count of quotes: %i", appDelegate.quotes.count);
NSLog(#"Count of quoteMaps: %i", appDelegate.quoteMaps.count);
}
Here is the getDbPath:
- (NSString *) getDBPath {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory , NSUserDomainMask, YES);
NSString *documentsDir = [paths objectAtIndex:0];
return [documentsDir stringByAppendingPathComponent:#"reference.db"];
}
Sometimes, the best thing to do is build->analyze ( cmd shift b ). This will point out your bug right away in almost all cases.
Best of luck!
categories = [[NSMutableArray alloc] init];
.
.
//Initialize the Category array.
NSMutableArray *tempArray = [[NSMutableArray alloc] init];
self.categories = tempArray;
[tempArray release];
u've setup categories, then setup the tempArray, replaced the one in categories with it thus making a leak, then released the temp arrayObject, which what categories is now also pointing on, so unless "self.categories" is a retained property it will be a zombie. there seems to be something wrong there.
I may need to see some more of your code (the property declarations and their synthesis to make sure.
is the Zombie called on "getInitialDataToDisplay" or on "getDBPath"
try dividing it on 2 lines to know pin point more
I think you have not declare Category as retained property in .h file.If not, add following line in your .h file
#property (nonatomic, retain) NSArray Category;
And synthesize the property in .m as-
#synthesize Category;
I think it will help ....
I am using a Window based application and then loading up my initial navigationview based controller in the appDelegate part - application didFinishLaunchingwithOptions (UPDATED)
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Override point for customization after application launch.
self.window.rootViewController = self.navigationController;
[self.window makeKeyAndVisible];
return YES;
}
The method of getting the data from the webservice is usually triggered in this method of viewdidload .... in the [self getData]; method.
- (void)viewDidLoad
{
[super viewDidLoad];
if (_refreshHeaderView == nil) {
EGORefreshTableHeaderView *view = [[EGORefreshTableHeaderView alloc] initWithFrame:CGRectMake(0.0f, 0.0f - self.tableView.bounds.size.height, self.view.frame.size.width, self.tableView.bounds.size.height)];
view.delegate = self;
[self.tableView addSubview:view];
_refreshHeaderView = view;
[view release];
}
// update the last update date
[_refreshHeaderView refreshLastUpdatedDate];
[self loadImages];
HUDMB = [[MBProgressHUD alloc] initWithView:self.view];
[self.view addSubview:HUDMB];
HUDMB.dimBackground = YES;
// Regiser for HUD callbacks so we can remove it from the window at the right time
HUDMB.delegate = self;
HUDMB.labelText = #"Loading..";
[HUDMB show:TRUE];
[self getData];
[self.tableView reloadData];
}
Before loading it if the user is not registered/ does not have a credentials present then it takes the user to a login view controller .
- (void)loadView {
[super loadView];
if([Preferences isValid]?YES:NO)
{
}
else
{
int r = arc4random() % 5;
switch (r) {
case 0:
{
loginViewController *sampleView = [[loginViewController alloc] initWithNibName:#"loginViewController" bundle:nil];
[self.navigationController presentModalViewController:sampleView animated:YES];
[sampleView release];
}
break;
case 1:
{
loginViewController *sampleView = [[loginViewController alloc] initWithNibName:#"loginViewController" bundle:nil];
[sampleView setModalTransitionStyle:UIModalTransitionStyleFlipHorizontal];
[self.navigationController presentModalViewController:sampleView animated:YES];
[sampleView release];
}
break;
case 2:
{
loginViewController *sampleView = [[loginViewController alloc] initWithNibName:#"loginViewController" bundle:nil];
[sampleView setModalTransitionStyle:UIModalTransitionStyleCrossDissolve];
[self.navigationController presentModalViewController:sampleView animated:YES];
[sampleView release];
}
break;
case 4:
{
loginViewController *sampleView = [[loginViewController alloc] initWithNibName:#"loginViewController" bundle:nil];
[sampleView setModalTransitionStyle:UIModalTransitionStylePartialCurl];
[self.navigationController presentModalViewController:sampleView animated:YES];
[sampleView release];
}
break;
case 3:
{
loginViewController *sampleView = [[loginViewController alloc] initWithNibName:#"loginViewController" bundle:nil];
[sampleView setModalTransitionStyle:UIModalTransitionStyleCoverVertical];
[self.navigationController presentModalViewController:sampleView animated:YES];
[sampleView release];
}
break;
default:
break;
}
}
}
I think the getdata function is making a lot of troubles. so let me add that and also the corresponding functions i use for data retrieval and serialization.
-(void)getData{
NSLog(#"loggin into call sheet page");
[self getCallSheetData];
NSLog(#"after call sheet");
}
- (void)getCallSheetData
{
NSString *postCMD = #"Blah... Blah... Blah...";
NSMutableData *postDataCMD = (NSMutableData *)[postCMD dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES];
NSURL *url = [NSURL URLWithString:[Preferences getURL]];
// NSLog(#"get call Sheet");
NSString *postLengthCMD = [NSString stringWithFormat:#"%d", [postDataCMD length]+1];
// NSLog(#"The CS String: %#",[Preferences retriveSession]);
requestCMD = [ASIHTTPRequest requestWithURL:url];
[requestCMD setURL:url];
[requestCMD addRequestHeader:#"Content-Length" value:postLengthCMD];
[requestCMD addRequestHeader:#"Content-Type" value:#"application/x-www-form-urlencoded"];
[requestCMD addRequestHeader:#"user-Agent" value:#"Mobile 1.4" ];
[requestCMD addRequestHeader:#"Content-Language" value:#"en-US"];
[requestCMD addRequestHeader:#"Accept-Encoding" value:#"gzip"];
[requestCMD addRequestHeader:#"Cookie" value:[Preferences retriveSession]];
[requestCMD setPostBody:postDataCMD];
[requestCMD setDelegate:self];
[requestCMD startAsynchronous];
}
- (void)requestFinished:(ASIHTTPRequest *)request
{
NSMutableArray *CSArray = [[NSMutableArray alloc] init];
if( [[request responseString] isEqualToString:#"OK"]){
return;
}
// Use when fetching binary data
NSData *responseData = [request responseData];
NSDateFormatter *formatter1=[[NSDateFormatter alloc]init];
[formatter1 setDateFormat:#"yyyy-MM-dd'T'HH:mm:ss'Z'"];
NSTimeZone *gmt = [NSTimeZone timeZoneWithAbbreviation:#"GMT"];
[formatter1 setTimeZone:gmt];
NSDateFormatter *formatterFinal=[[NSDateFormatter alloc]init];
[formatterFinal setDateStyle:NSDateFormatterMediumStyle];
[formatterFinal setTimeStyle: NSDateFormatterShortStyle];
[formatterFinal setLocale:[NSLocale currentLocale]];
JSONDecoder *jCSArray = [[JSONDecoder alloc]init];
NSMutableArray *theObject = [jCSArray objectWithData:responseData];
// CallSheet= [NSMutableArray arrayWithCapacity:50];
for(id key in theObject)
{
csr = [[CallSheetRecord alloc] init];
// cName,cCompany,cId,cMemberId,crcStatus,crcTarget,cImportance,cLastContact
csr.importance = #"1";
csr.rcstatus = #"1";
csr.rcTarget = #"1";
csr.company = #"";
csr.lastContact= #"";
if([key valueForKey:#"firstName"] != Nil)
{
csr.name = [NSString stringWithFormat:#"%#",[key valueForKey:#"firstName"]] ;
if ([key valueForKey:#"lastName"] != Nil) {
csr.name = [csr.name stringByAppendingString:#" "];
csr.name = [csr.name stringByAppendingString:[NSString stringWithFormat:#"%#",[key valueForKey:#"lastName"]]];
}
}
if([key valueForKey:#"company"] != Nil)
{
csr.company = [NSString stringWithFormat:#"%#",[key valueForKey:#"company"]] ;
}
if([key valueForKey:#"memberId"] != Nil)
{
csr.memberId = [NSString stringWithFormat:#"%#",[key valueForKey:#"memberId"]] ;
}
if([key valueForKey:#"id"] != Nil)
{
csr.id_ = [NSString stringWithFormat:#"%#",[key valueForKey:#"id"]] ;
}
if([key valueForKey:#"lastContact"] != Nil)
{
NSDate *finalDate =[formatter1 dateFromString:[NSString stringWithFormat:#"%#",[key valueForKey:#"lastContact"]]];
//NSString *timeStamp = [formatter1 stringFromDate:[finalDate descriptionWithLocale:[NSLocale currentLocale]]];
//NSLog(#"Time stamp : %#",[finalDate descriptionWithLocale:[NSLocale currentLocale]]);
//NSLog(#"Time stamp : %#",timeStamp);
//csr.lastContact = [key valueForKey:#"lastContact"];
csr.lastContact = [formatterFinal stringFromDate:finalDate];
}
if([key valueForKey:#"importance"] != Nil)
{
csr.importance = [NSString stringWithFormat:#"%#",[key valueForKey:#"importance"]];
}
if([key valueForKey:#"rcStatus"] != Nil)
{
csr.rcstatus= [NSString stringWithFormat:#"%#",[key valueForKey:#"rcStatus"]] ;
}
if([key valueForKey:#"rcTarget"] != Nil)
{
csr.rcTarget = [NSString stringWithFormat:#"%#",[key valueForKey:#"rcTarget"]] ;
}
[CSArray addObject:csr];
}
CSD = [CSArray mutableCopy];
[CSArray release];
[formatter1 release];
[formatterFinal release];
//CallSheetArray = [CSArray mutableCopy];
//[csr release];
[jCSArray release];
[HUDMB hide:TRUE];
[self.tableView reloadData];
}
- (void)requestFailed:(ASIHTTPRequest *)request
{
NSError *error = [request error];
UIAlertView *message = [[[UIAlertView alloc] initWithTitle:#"Hello World!"
message:[NSString stringWithFormat:#"%#",error]
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil]autorelease];
[message show];
}
then right after that i try to load the table with [self getData]; that i get make using webservice call using asiHTTP .. for this question lets say it takes 3 seconds time to get the data and then deserialize it . now... my question is it works out okey in the later runs as I store the username and password in a seure location... but in the first instance.... i am not able to get the data to laod to the tableview... I have tried a lot of things...
1. Initially the data fetch methods was in a diffrent methods.. so i thought that might be the problem as then moved it the same place as the tbleviewController(navigationController)
2. I event put in the Reload data at the end of the functionality for the data parsing and deserialization... nothing happens.
3. The screen is black screen with nothing displayed on it for a good 5 seconds in the consecutive launches of the app.... so could we have something like a MBPorgressHUD implemented for the same.
could any one please help for these scenarios and guidance as to what paths to take from here. ...
Update:
Bounty Just answer 2 of the questions the best you can... The property was a thing i tried but it did not work... but my problem is not that... my problem is my screen is not able to load data from a webservice after i login until i do the "pull to refresh". Next eventualy starting my app takes about 5 seconds to show the screen(till that it shows a black screen)... what is the best way to show the apps Blank screen to the End- user when they make it to the app. I dont want the user to think the app is not working or has adversly affected their phone.
There are a number of potential issues here. The first is related to [self getData] - is this blocking or not?
1. getData is blocking (synchronous)
If this method is blocking (i.e. does not return immediately) then this will explain why you are not seeing anything on the screen for a few seconds. You are currently calling this method in the main thread, so it will stop the UI from being updated until is complete.
You can make it run in the background by doing this:
- (void) getDataInBackgroundThread
{
NSAutoReleasePool *pool = [[NSAutoReleasePool alloc] init];
[self getData];
// force the table to redraw - UI operations must be on the main thread
[tableView performSelectorOnMainThread:#selector( reloadData )];
[pool drain];
}
In your main code, now instead of calling [self getData] call [self getDataInBackgroundThread];
2. getData is not blocking (asynchronous)
Even if getData is blocking, if it runs in the main thread it will still cause the UI to hang. Also, if it uses a lot of processor time it will slow down the UI and give the appearance of not running in the background.
To address this issue, you would need make sure the lengthy operation of getData really is not running in the main thread and also put in sleep() calls every so often to give some time for the UI to update.
You still haven't given enough info for people to really help, we're just guessing.
It is highly likely the delay is caused by getData. Try commenting that out or bracket it with NSLog statements to verify.
You should show us more code, specifically what is happening in getData and any delegate or notification handling implementations related to loading data.
You don't need to explicitly call [tableView reloadData] in viewDidLoad. The tableView automatically loads data the first time it is displayed.
As #dmatt said, if you are loading data asynchronously then getData would return immediately with no delay. You would typically would either respond to delegate messages or notifications (depending on how you are loading data) to reload the table when the data is finished loading and serializing.
There are lots of folks on SO who are happy to try and help you if given enough info. Especially when you offer a bounty.
So what I'm trying to do is when you click view on receiving a push notification when the app opens it has the presented view push a new controller with details regarding the notification. I'm using a UITabBarController with UINavigationControllers. Any help would be much appreciated, I've tried searching but I couldn't seem to find anything that pointed me in the right direction. Current code below:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[[UIApplication sharedApplication] registerForRemoteNotificationTypes:(UIRemoteNotificationTypeAlert | UIRemoteNotificationTypeSound)];
[application setStatusBarStyle:UIStatusBarStyleBlackOpaque];
tabBarController = [[UITabBarController alloc] init];
controller = [[controller alloc] init];
UINavigationController *controller1 = [[[UINavigationController alloc] initWithRootViewController:controller] autorelease];
controller1.tabBarItem.image = [UIImage imageNamed:#"icon_news.png"];
[controller setTitle:#"View"];
[controller release];
controller = [[controller alloc] init];
UINavigationController *controller2 = [[[UINavigationController alloc] initWithRootViewController:controller] autorelease];
controller2.tabBarItem.image = [UIImage imageNamed:#"icon_news.png"];
[controller setTitle:#"View"];
[controller release];
controller = [[controller alloc] init];
UINavigationController *controller3 = [[[UINavigationController alloc] initWithRootViewController:controller3] autorelease];
controller3.tabBarItem.image = [UIImage imageNamed:#"icon_news.png"];
[controller setTitle:#"View"];
[controller release];
controller = [[controller alloc] init];
UINavigationController *controller4 = [[[UINavigationController alloc] initWithRootViewController:controller] autorelease];
controller4.tabBarItem.image = [UIImage imageNamed:#"icon_news.png"];
[controller setTitle:#"View"];
[controller release];
controller = [[controller alloc] init];
UINavigationController *controller5 = [[[UINavigationController alloc] initWithRootViewController:controller] autorelease];
controller5.tabBarItem.image = [UIImage imageNamed:#"icon_news.png"];
[controller setTitle:#"View"];
[controller release];
tabBarController.viewControllers = [NSArray arrayWithObjects:controller1, controller2, controller3, controller4, controller5, nil];
[window addSubview:tabBarController.view];
[window makeKeyAndVisible];
launchDefault = YES;
//[self performSelector:#selector(handlePostLaunch) withObject:nil afterDelay:0];
// Push Notification info
NSDictionary *apns = [launchOptions objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey];
NSString *result = [[[apns valueForKey:#"aps"] valueForKey:#"alert"] valueForKey:#"loc-args"];
NSString *playerID = [NSString stringWithFormat:#"%#", result];
playerID = [[playerID componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]] componentsJoinedByString:#""];
playerID = [playerID stringByReplacingOccurrencesOfString:#" " withString:#""];
playerID = [playerID stringByReplacingOccurrencesOfString:#"(" withString:#""];
playerID = [playerID stringByReplacingOccurrencesOfString:#")" withString:#""];
NSLog(#"Player ID: %#", playerID);
if (![playerID isEqualToString:#"null"]) {
if (!detailViewController) {
detailViewController = [[PlayerDetailViewController alloc] init];
}
NSManagedObjectContext *moc = [[AppController sharedAppController] managedObjectContext];
NSFetchRequest *req = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Players"
inManagedObjectContext:moc];
[req setEntity:entity];
NSPredicate *pre = [NSPredicate predicateWithFormat:#"playerID=%#", playerID];
[req setPredicate:pre];
NSError *error;
NSArray *list = [moc executeFetchRequest:req error:&error];
[req release];
Players *player = [list lastObject];
[detailViewController setPlayer:player];
//Want to Push view here
[detailViewController release];
detailViewController = nil;
}
return YES;
}
If application is launched by a notification then it will be present in the application delegate's application:didFinishLaunchingWithOptions: launchOptions dictionary under the key UIApplicationLaunchOptionsRemoteNotificationKey, which has all the info the notification has (JSON converted to NSDictionary I believe).
EDIT:
Got the question wrong, I think what you're looking for is just the pointer to the currently selected navigation controller. You get that if you query [tabbarcontroller selectedViewController], which returns the visible navigation controller. Then simply push the newly created controller on top of the stack of that navigation controller.
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.