Memory problem in NSXMLParser (iPhone) - iphone

Hi I'm trying to parse an xml and use the currentElementValue inside a code to get an expiredate. This is the code.
if([elementName isEqualToString:#"utlop"]) {
NSDate *now = [NSDate dateWithTimeIntervalSinceNow:0];
NSDateFormatter *dateFormat = [[NSDateFormatter alloc] init];
[dateFormat setDateStyle:NSDateFormatterShortStyle];
int numberOfDays = [currentElementValue intValue];
NSDate *expireDate = [now addTimeInterval:60*60*24*numberOfDays];
NSString *expireString = [dateFormat stringFromDate:expireDate];
NSLog(#"ExpiryString :%#", expireString);
//Add values to Vare
enVare.utlop = expireString;
enVare.enhet = enhet;
enVare.isDirty = NO;
//Add Vare
[appDelegate addVare:enVare];
//Releasing
[dateFormat release];
[enVare release];
enVare = nil;
[currentElementValue release];
currentElementValue = nil;
[expireString release];
expireString = nil;
This results in a memory leak, but Im new to objective C so I can't find the error. When I just do this, it works:
enVare.utlop = currentElementValue;

Do not release objects that are not owned by you. You own an object when u create them using new or alloc. Release only those objects that are created by you using these functions. Also make sure that you release such objects once you have finished using them.

Related

Objective C: Troubles archiving an NSDate

I'm attempting to save an altered NSDate (8am of the start date) in a database to be retrieved anytime the program is run. I am using object archiving. I thought I had the right code, but I can't seem to get it to save. I receive no errors, just the output I've put into my code. I know the date and time is correct because they are seen as output in NSLog. Here is my code:
__dataArea = [NSMutableData data];
__unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:__dataArea];
__archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:__dataArea];
__iDates = [[BCimportantDates alloc] initWithCoder:[NSKeyedUnarchiver unarchiveObjectWithFile: #"firstDate.arch"]];
if ((__iDates.firstDate == nil)){
NSDate *date = [NSDate date];
NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier: NSGregorianCalendar];
NSDateComponents *components = [gregorian components: NSUIntegerMax fromDate: date];
NSLog(#"the date %#",date);
[components setHour: 3];
[components setMinute: 00];
[components setSecond: 00];
__newDate = [gregorian dateFromComponents: components];
[__iDates setFirstDate: __newDate];
NSLog(#"%#",__iDates.firstDate);
[__iDates encodeWithCoder: __archiver];
[__archiver finishEncoding];
if ([__dataArea writeToFile:#"firstDate.arch" atomically:YES] == NO){
NSLog(#"archiving failed. ");
}
}
And here is the implementation of the encoder and decoder functions within BCimportantDates.m:
- (void) encodeWithCoder:(NSCoder *)encoder{
[encoder encodeObject: __firstDate forKey: kfirstDateKey];
}
- (id) initWithCoder: (NSCoder *) decoder{
if (self = [super init]) {
self.firstDate = [decoder decodeObjectForKey:kfirstDateKey];
}
return self;
}
I've tried using breakpoints where __iDates is encoded, where the archiver finishes, and where I check if it worked. The debugging was not that revealing, but to be honest I'm not sure what to look for when it comes to finding this kind of error. What else can I do to figure out this problem? What might be some possible solutions?
I think the problem here is that you are not specifying a path for writeToFile: to write to.
Try this:
NSString *path = [NSTemporaryDirectory() stringByAppendingPathComponent:#"firstDate.arch"];
if ([__dataArea writeToFile:path atomically:YES] == NO){
NSLog(#"archiving failed. ");
}
Which will write your file to a temporary directory, you can simply set a breakpoint or log the path variable to find out where this location is. NSTemporaryDirectory() is simply for an example though, as this folder is only temporary and can be deleted by the system at any time. Here is a category on NSFileManager which may provide you with a more appropriate path.

random BAD ACCESS when saving user defaults

I'm simply trying to save this array to the user defaults and it will crash at random. Sometimes it works, sometimes it gives me the EXC_BAD_ACCESS. Am I not releasing something properly?
- (void)setTextValue:(NSString *)valueText indexToSet:(NSUInteger)index
{
[self.pageData replaceObjectAtIndex:index withObject:valueText];
[[NSUserDefaults standardUserDefaults] setObject:self.pageData forKey:#"mynotes"];
}
Here is the method that i've determined is causing the errors. It was a method already created by Xcode that I added my own custom code to.
- (nbookDataViewController *)viewControllerAtIndex:(NSUInteger)index storyboard:(UIStoryboard *)storyboard
{
nbookDataViewController *dataViewController = [storyboard instantiateViewControllerWithIdentifier:#"nbookDataViewController"];
if (self.pageData.count > 0 && index < self.pageData.count)
{
NSString *val = (NSString *)[self.pageData objectAtIndex:index];
dataViewController.dataObject = val;
}
else
{
NSDate *date = [NSDate date];
NSDateFormatter *dateFormat = [[NSDateFormatter alloc] init];
[dateFormat setDateFormat:#"MMMM d, YYYY"];
NSString *dateString = [dateFormat stringFromDate:date];
//[dateFormat release];
[self.pageData addObject:dateString];
dataViewController.dataObject = (NSString *)[self.pageData objectAtIndex:index];
}
dataViewController.myModel = (nbookModelController *)self;
dataViewController.dIndex = index;
//[self.mySaveData setObject:self.pageData forKey:#"mynotes"];
return dataViewController;
}
This tip will allow you code to break on the exception and let you check directly why this is happening:
https://stackoverflow.com/a/616526/46970

iphone how to pass string to another view label?

My problem is how to pass string to another view label? I got try so many example but still can not get the value where I pass.
here is I save the data.
-(IBAction)Save:(id)sender{
timedata = [datePicker date];
NSLog(#"timedata save is = %#",timedata);
time = [NSString stringWithFormat:#"%#",timedata];
NSLog(#"String time = %#",time);
[self dismissModalViewControllerAnimated:YES];
}
here is I want to show the save data.
- (void) viewWillAppear:(BOOL)animated {
show = [[SelectDate alloc]initWithNibName:#"SelectDate" bundle:nil];
show.time = time;
NSLog(#"time = %#",time);
Selectime.text = show.time;
NSLog(#"show.time = %#",show.time);
[super viewWillAppear:animated];
}
If you have set property for time in SelectDate viewController so that it can be accessed in other viewControllers.
//SelectDate.h
NSString *time;
// Your declarations
#property(nonatomic,retain) NSString *time;
//SelectDate.m
#synthesize time;
Now you can use time in other ViewControllers like you are doing.
To get a NSString representation of a date you should look at NSDateFormatter
example:
NSDate* timedata = [datePicker date];
NSDateFormatter* dateFormat = [[NSDateFormatter alloc] init];
[dateFormat setDateFormat:#"dd/MM/yyyy"];
NSString *time = [dateFormat timedata];
[dateFormat release];
Instead of accessing last view, pass your data to your next view:
Hi,My problem is how to pass string to another view label? I got try so many example but still can not get the value where I pass.
here is I save the data.
-(IBAction)Save:(id)sender{
timedata = [datePicker date];
NSLog(#"timedata save is = %#",timedata);
time = [NSString stringWithFormat:#"%#",timedata];
NSLog(#"String time = %#",time);
[self dismissModalViewControllerAnimated:YES];
YourNextView *yourNextView=[[YourNextView alloc] init];
yourNextView.anString=[NSString stringWithFormat:#"%#",timedata];
[yourNextView release];
}
here is You want to show the save data.
- (void) viewWillAppear:(BOOL)animated {
//show = [[SelectDate alloc]initWithNibName:#"SelectDate" bundle:nil];
//show.time = time;
//NSLog(#"time = %#",time);
Selectime.text = self.anString;
NSLog(#"show.time = %#",show.time);
[super viewWillAppear:animated];
}

Optimise slow code - enumeration of dictionary

I have the following code that decodes a JSON string into an array of objects that I can then use in a UITableView.
At first I thought the JSON decoding was the slow part but it appears that it is not as the "Dictionary Done" appears almost immediately.
Any ideas on how to get that code to be a little faster?
-(void)parseJSON:(NSString *)jsonData{
NSLog(#"Start parsing");
NSDictionary *deserializedData = [jsonData objectFromJSONString];
NSLog(#"Dictionary Done");
NSArray *flights = [deserializedData valueForKeyPath:#"flights.flight"];
NSMutableArray *localArray = [[NSMutableArray alloc] init ];
NSString *lastFlightno =#"";
for (NSDictionary *flight in flights){
ArchiveFlight *aFlight = [[ArchiveFlight alloc] initWithFlightno:[flight objectForKey:#"flightno"] route:[flight objectForKey:#"route"]];
aFlight.flightID = [flight objectForKey:#"primary_key"];
aFlight.timeStamp = [aFlight niceDate:[flight objectForKey:#"timestamp"]];
if (![lastFlightno isEqualToString:aFlight.flightno]) {
[localArray addObject:aFlight];
}
lastFlightno =aFlight.flightno;
[aFlight release];
}
NSLog(#"End Parsing");
[self loadupTable:localArray];
self.flightArray = localArray;
[localArray release];
}
EDIT: Added timestamps
Timestamps of the NSLogs as follows...
2011-04-26 13:22:36.104 App[1778:707] Finished request
2011-04-26 13:22:36.109 App[1778:707] Start parsing
2011-04-26 13:22:36.128 App[1778:707] Dictionary Done
2011-04-26 13:22:37.713 App[1778:707] End Parsing
Sample of the JSON...
{"flights":[{"flight":{"flightno":"RYR54WP","timestamp":"2011-04-26 12:13:04","route":"EGNX-LEAL","primary_key":"836453"}},{"flight":{"flightno":"RYR24LU","timestamp":"2011-04-26 09:14:03","route":"EVRA-EGNX","primary_key":"831318"}},{"flight":{"flightno":"RYR39WH","timestamp":"2011-04-26 05:33:03","route":"EGNX-EVRA","primary_key":"825492"}},{"flight":{"flightno":"RYR7PX","timestamp":"2011-04-25 20:07:03","route":"LELC-EGNX","primary_key":"816703"}},{"flight":{"flightno":"RYR2VB","timestamp":"2011-04-25 16:57:06","route":"EGNX-LELC","primary_key":"810900"}},{"flight":{"flightno":"RYR3JN","timestamp":"2011-04-25 12:36:04","route":"GCTS-EGNX","primary_key":"802631"}},{"flight":{"flightno":"RYR8GV","timestamp":"2011-04-25 06:07:03","route":"EGNX-GCTS","primary_key":"792945"}},{"flight":{"flightno":"RYR82QR","timestamp":"2011-04-24 19:42:04","route":"EPKK-EGNX","primary_key":"783306"}},{"flight":{"flightno":"RYR51PV","timestamp":"2011-04-24 16:31:05","route":"EGNX-EPKK","primary_key":"777835"}},{"flight":{"flightno":"RYR53AQ","timestamp":"2011-04-24 14:09:05","route":"LIME-EGNX","primary_key":"773572"}},{"flight":{"flightno":"RYR1CX","timestamp":"2011-04-24 11:02:05","route":"EGNX-LIME","primary_key":"768285"}},{"flight":{"flightno":"RYR9ZW","timestamp":"2011-04-24 08:21:04","route":"LEGE-EGNX","primary_key":"764624"}},{"flight":{"flightno":"RYR63BC","timestamp":"2011-04-24 05:48:02","route":"EGNX-LEGE","primary_key":"761726"}},{"flight":{"flightno":"RYR7PX","timestamp":"2011-04-23 19:39:03"
Formatted sample:
{
"flights":[
{
"flight":{
"flightno":"RYR54WP",
"timestamp":"2011-04-26 12:13:04",
"route":"EGNX-LEAL",
"primary_key":"836453"
}
},
{
"flight":{
"flightno":"RYR24LU",
"timestamp":"2011-04-26 09:14:03",
"route":"EVRA-EGNX",
"primary_key":"831318"
}
}
]
}
EDIT 2:
So here is "niceDate" that is causing the slowdown!
-(NSString *)niceDate:(NSString *)oldDate{
NSDateFormatter *formatter = [[[NSDateFormatter alloc] init]autorelease];
[formatter setDateFormat:#"yyyy-MM-dd HH:mm:ss"];
NSDate *sourceDate = [formatter dateFromString:oldDate];
NSDateFormatter *dateFormatter = [[[NSDateFormatter alloc] init] autorelease];
[dateFormatter setDateStyle:NSDateFormatterFullStyle];
[dateFormatter setTimeStyle:NSDateFormatterLongStyle];
NSString *timeString = [dateFormatter stringFromDate:sourceDate];
return [NSString stringWithFormat:#"%#",timeString];
}
Some things that come to mind:
NSArray *flights = [deserializedData valueForKeyPath:#"flights.flight"];
Do you need to use KVC? What is the structure of your JSON data?
ArchiveFlight *aFlight = [[ArchiveFlight alloc] initWithFlightno:[flight objectForKey:#"flightno"] route:[flight objectForKey:#"route"]];
aFlight.flightID = [flight objectForKey:#"primary_key"];
aFlight.timeStamp = [aFlight niceDate:[flight objectForKey:#"timestamp"]];
You always create an instance of ArchiveFlight and parse the timestamp…
if (![lastFlightno isEqualToString:aFlight.flightno]) {
[localArray addObject:aFlight];
}
…even though you don’t have to do that all the time. Depending on how many repeated flightnos you have, this can make a noticeable difference.
Why not read [flight objectForKey:#"flightno"], compare it to lastFlightno and, if and only if they’re different, create an instance, add it to the array, and release it?
Edit: Try the following KVC-free code:
NSArray *flights = [deserializedData objectForKey:#"flights"];
NSMutableArray *localArray = [[NSMutableArray alloc] init ];
NSString *lastFlightno =#"";
for (NSDictionary *flightWrapper in flights) {
NSDictionary *flight = [flightWrapper objectForKey:#"flight"];
NSString *flightno = [flight objectForKey:#"flightno"];
if (! [flightno isEqual:lastFlightno]) {
// create instance, add it to the array, release the instance
}
}
Edit: You’re creating and (auto)releasing two instances of NSDateFormatter inside this method. In general this would be okay, but since it is being executed >1K times there are two considerations: a) you’re creating/using/releasing those two instances >1K times when, in fact, you don’t have two, b) you should use an autorelease pool in your loop.
You should make this method a class method (or a function) since it doesn’t depend on the state of an instance of that class. Your formatters would be class (static) variables. For instance:
#implementation ArchiveFlight
static NSDateFormatter *formatter1; // choose better names!
static NSDateFormatter *formatter2;
+ (void)initialize {
if (self == [ArchiveFlight class]) {
formatter1 = [[NSDateFormatter alloc] init];
[formatter1 setDateFormat:#"yyyy-MM-dd HH:mm:ss"];
formatter2 = [[NSDateFormatter alloc] init];
[formatter2 setDateStyle:NSDateFormatterFullStyle];
[formatter2 setTimeStyle:NSDateFormatterLongStyle];
}
}
+ (NSString *)niceDate:(NSString *)oldDate {
NSDate *sourceDate = [formatter1 dateFromString:oldDate];
NSString *timeString = [formatter2 stringFromDate:sourceDate];
return timeString;
// why +stringWithFormat:? It’s not necessary!
// return [NSString stringWithFormat:#"%#",timeString];
}
This fixes item a) but you really should use an autorelease pool inside your loop because the Cocoa methods you’re using return autoreleased objects. By using an autorelease pool for each iteration of your loop, you reduce the memory footprint of your code — although this could decrease performance as well. I suggest you try both with and without an inner autorelease pool.
for (NSDictionary *flightWrapper in flights) {
NSAutoreleasePool *pool = [NSAutoreleasePool new];
…
[pool drain];
}

Core Data saves object even when validation fails

I've included the function that is handling all of the save functionality.
Here's my problem.
I am grabbing 5 input values and saving it as a CoreData Log Entity.
Even when the Log object fails to validate, it is still being saved when I back out of the form and look at the table view.
How can I force Core Data to only save the object once it's validated?
-(void) saveLog {
NSManagedObjectContext *managedObjectContext = [(AppDelegate_Shared *)[[UIApplication sharedApplication] delegate] managedObjectContext];
FormPickerCell *bloodPressure = (FormPickerCell *) [self.formController fieldAsObject:#"bloodpressure"];
NSInteger systolic = [(PressureDataSource*)bloodPressure.pickerCellDelegate selectedSystolicPressureForFormPickerCell:bloodPressure];
NSInteger diastolic = [(PressureDataSource*)bloodPressure.pickerCellDelegate selectedDiastolicPressureForFormPickerCell:bloodPressure];
NSLog(#"bp is %d / %d", systolic, diastolic);
NSLog(#"date is %#", [self.formController valueForField:#"date"]);
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"yyyy-MM-dd HH:mm:ss ZZZ"];
if (self.isNewLog && !self.validationHasFailed) {
self.log = [NSEntityDescription
insertNewObjectForEntityForName:#"Log" inManagedObjectContext:managedObjectContext];
}
NSString *heartRate = [[self.formController valueForField:#"heartrate"] stringByReplacingOccurrencesOfString:#" bpm" withString:#""];
NSNumberFormatter * f = [[NSNumberFormatter alloc] init];
[f setNumberStyle:NSNumberFormatterDecimalStyle];
self.log.created = [NSDate date];
self.log.notes = [self.formController valueForField:#"notes"];
self.log.systolic = [NSNumber numberWithInteger:systolic];
self.log.diastolic = [NSNumber numberWithInteger:diastolic];
self.log.stressLevel = [self.formController valueForField:#"stresslevel"];
self.log.logDate = [dateFormatter dateFromString:[self.formController valueForField:#"date"]];
self.log.heartrate = [f numberFromString:heartRate];
NSLog(#"Log date is %#",[self.formController valueForField:#"date"]);
[f release];
NSError *error;
NSString *title;
NSString *growlDescription;
if ([self.log validateForInsert:&error]){
NSLog(#"after validation returned true");
if(![managedObjectContext save:&error]) {
NSLog(#"Unresolved error");
title = #"Error Occurred";
growlDescription = [error localizedDescription];
self.validationHasFailed = YES;
} else {
title = #"Log Saved!";
growlDescription = #"Log saved successfully";
[self.navigationController popViewControllerAnimated:YES];
}
} else {
NSLog(#"after validation returned false");
NSLog(#"Unresolved error");
title = #"Error Occurred";
growlDescription = [error localizedDescription];
self.validationHasFailed = YES;
}
IZGrowlNotification *notification = [[IZGrowlNotification alloc] initWithTitle:title
description:growlDescription
image:nil
context:nil
delegate:self];
[[IZGrowlManager sharedManager] postNotification:notification];
[notification release];
error = nil;
}
This is a bit late but I just saw your question so figured I'd toss an answer at you. Any object you add to the managed object context will be saved whenever you next save. You could leave your code as is and just delete the new object with [managedObjectContext deleteObject:self.log] but a better method is below.
Your code:
self.log = [NSEntityDescription insertNewObjectForEntityForName:#"Log" inManagedObjectContext:managedObjectContext];
Creates a new Log instance and inserts into the managed object context. What you want to do instead is:
self.log = [[Log alloc] initWithEntity:[NSEntityDescription entityForName:#"Log" inManagedObjectContext:managedObjectContext] insertIntoManagedObjectContext:nil];
This will create a new 'Log' instance that has not yet been inserted into the MOC. If validation succeeds, before you save the MOC you insert the self.log as follows:
[managedObjectContext insertObject:self.log];
Then you save. If validation fails, don't insert the object and you're good to go.