iPhone PList and NSDate issues - iphone

I am doing:
NSString *path = [[self class] pathForDocumentWithName:#"Alarms.plist"];
NSArray *alarmDicts = [NSMutableArray arrayWithContentsOfFile:path];
if (alarmDicts == nil)
{
NSLog(#"MER. Unable to read plist file: %#", path);
path = [[NSBundle mainBundle] pathForResource:#"Alarms"
ofType:#"plist"];
alarmDicts = [NSMutableArray arrayWithContentsOfFile:path];
}
_displayedObjects = [[NSMutableArray alloc]
initWithCapacity:[alarmDicts count]];
for (NSDictionary *currDict in alarmDicts)
{
Alarm *alarm = [[Alarm alloc] initWithDictionary:currDict];
[_displayedObjects addObject:alarm];
}
pathForDocumentWithName just a helper method, assume it works (it does). I add all of the values of the plist to an object, and store it in an array. Now if I do something like this:
NSUInteger index = [indexPath row];
id alarm = [[self displayedObjects] objectAtIndex:index];
NSString *title = [alarm title];
[[cell detailTextLabel] setText:title];
It works perfectly fine. But when trying to format the NSDate type in the plist file (listed as 'datetime')
NSDate *datetime = [alarm datetime];
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setDateFormat:#"hh:mm"];
[[cell textLabel] setText:[formatter stringFromDate:datetime]];
It throws the NSLog for alarmDicts being nil, and returns nil for the string. I'm out of ideas, and have been trying for a few hours to solve this. Anyone have any ideas?
Also, if I print out the description for datetime, it works perfectly. Only nils and errors out when I attempt to use the NSDateFormatter on it.

A massive guess, but are you sure the dates in your property list are being read in as NSDate objects? If I were you, I'd check the type of your apparent NSDate objects, e.g.
NSLog(#"%#", [[alarm datetime] class]);
I would be suspicious that they're being loaded as NSStrings, which NSDateFormatter will decline to process — but they'll still appear to log correctly.
Unrelated comment: I'm sure it's a copy and paste error only, but you're leaking 'Alarm' objects at the bottom of your first snippet of code.

Related

conversion Nsstring

Hi all guys ,I am new in objective c and I need help, I read from my xml
file and I want convert my NSString to bool and NSString to date and
Nsstring to long
NSArray *names = [partyMember elementsForName:#"price"];
if (names.count > 0) {
GDataXMLElement *firstName = (GDataXMLElement *) [names objectAtIndex:0];
price = firstName.stringValue;
} else continue;
NSArray *names1 = [partyMember elementsForName:#"date"];
if (names1.count > 0) {
GDataXMLElement *firstName1 = (GDataXMLElement *) [names1 objectAtIndex:0];
date = firstName1.stringValue;
NSArray *names1 = [partyMember elementsForName:#"test"];
if (names1.count > 0) {
GDataXMLElement *firstName1 = (GDataXMLElement *) [names1 objectAtIndex:0];
test = firstName1.stringValue;
For the BOOL
A string is NO if its either nil or length 0. Otherwise its YES.
NSDateFormatter's dateFromString will convert strings to dates. You set it up with a c style formatter.
For the long use longValue as in long long myval = [mystring longLongValue];
NSString has several converters
– doubleValue
– floatValue
– intValue
– integerValue
– longLongValue
– boolValue
Use as required.
In the future, please first look at Apple's documentation. It is very thorough. In the page on NSString, you can see there is a boolValue method and a longLongValue method. You can read the specifics in the documentation, but those will handle your bool and long cases.
As for converting a date, there are many stackoverflow questions on that topic, this one here should answer your question.
I'm usually not one to say RTFM, but in this case the information was very easily found with a couple quick searches.
To convert NSString to BOOL use below method.
BOOL boolValue = [myString boolValue];
For converting to long use longLongValue: method of NSString.
For NSString to NSDate , use below as reference code.
NSString *dateString = #"2011-07-13";
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"yyyy-MM-dd"];
NSDate *dateFromString = [[NSDate alloc] init];
dateFromString = [dateFormatter dateFromString:dateString];
NSString itself has functions to get bool and float values.
See reference.
To get date, u need to look at NSDateFormatter.

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];
}

Memory problem in NSXMLParser (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.

preparing a core data result set for a grouped uitableview

i've got a NSMutableArray created from a data source object
NSMutableArray *mutableFetchResults = [[managedObjectContext executeFetchRequest:request error:&error] mutableCopy];
[self setAmountArray: mutableFetchResults];
every object in my mutable array has the two instance variables name and timeadded.
now i want to display all names in a uitableview grouped by the day they are added. for that i wrote the following method
-(NSMutableArray*)arrangeTransfersByDate:(NSMutableArray*)transferArray {
// Setting up objects for this method
NSDate *oldDate = [NSDate dateWithTimeIntervalSince1970:0.0f];
NSDateFormatter *dateComparisonFormatter = [[NSDateFormatter alloc] init];
[dateComparisonFormatter setDateFormat:#"yyyy-MM-dd"];
NSMutableArray *returnArray = [[NSMutableArray alloc] init];
for(transfers *transfer in transferArray) {
if( [[dateComparisonFormatter stringFromDate:[transfer timeadded]] isEqualToString:[dateComparisonFormatter stringFromDate:oldDate]] ) {
if([returnArray count] == 0) {
[returnArray addObject:[NSMutableArray arrayWithObject:transfer]];
} else {
[[returnArray objectAtIndex:[returnArray count]-1] addObject:transfer];
}
} else {
[returnArray addObject:[NSMutableArray arrayWithObject:transfer]];
oldDate = [transfer timeadded];
}
}
//[returnArray release];
[dateComparisonFormatter release];
return returnArray;
}
transferArray is my amountArray where my core data objects are stored.
so this works! but
is there a better way to do this? can you give me something like a "best practise" or simply have a look if there are some memory leaks?
thanks!
edit:
the right answer was NSFetchedResultController and its sectionNameKeyPath.
however i don't wanted to store my data twice a time.
so i created the following getter method in my NSManagedObject.
- (NSString *) pubDate {
[self willAccessValueForKey:#"pubDate"];
NSDateFormatter *dateComparisonFormatter = [[NSDateFormatter alloc] init];
[dateComparisonFormatter setDateFormat:#"dd.MM.YYYY"];
NSString *temp = [dateComparisonFormatter stringFromDate:[self pubTime]];
[dateComparisonFormatter release];
[self didAccessValueForKey:#"pubDate"];
return temp;
}
with this i can sort my tableviewcontroller by date using my FetchedResultController and my pubTime which is a timestamp.
[[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
managedObjectContext:[self managedObjectContext]
sectionNameKeyPath:#"pubDate"
cacheName:#"transfersRoot"];
thanks to all
Generally when you want to display the results of a Core Data fetch in a UITableView, you use an NSFetchedResultsController. You can choose the attribute by which you want to group your results by specifying a sectionNameKeyPath during initialization.
That said, provided your method works then really it's up to you whether you want to change your code around or not.
But please make sure you [returnArray autorelease] before you return it. It's a generally accepted Objective-C practice that any method without "alloc", "new", or "copy" in the name will return an autoreleased object.

NSArray runtime array

I have got I have got two methods both in different classes. One is class method and other is instance method. i am calling class method from instance method. When instance method finishes it gives runtime error "EXC_BAD_ACCESS".
#import "xmlObject.h"
#import "textmeAppDelegate.h"
#implementation Class1
- (void)method1 {
textmeAppDelegate *del = (textmeAppDelegate *)[[UIApplication sharedApplication] delegate];
NSArray *bgColor = [[NSArray alloc] initWithArray:[xmlObject fetchImmediateChildrenValues:[del.navigationbarStyle objectForKey:#"backgroundcolor"]]];
UIColor *color = [UIColor colorWithRed:[[bgColor objectAtIndex:3] floatValue] green:[[bgColor objectAtIndex:2] floatValue] blue:[[bgColor objectAtIndex:1] floatValue] alpha:[[bgColor objectAtIndex:0] floatValue]];
CGContextSetFillColor(context, CGColorGetComponents([color CGColor]));
CGContextFillRect(context, rect);
[bgColor release];
}
#end
#implementation xmlObject
+ (NSArray *) fetchImmediateChildrenValues:(NSMutableDictionary *) node {
NSMutableDictionary *tmp = [[node objectForKey:#"children"] retain];
NSArray *keys = [[NSArray alloc] initWithArray:[tmp allKeys]];
keys = [keys sortedArrayUsingSelector:#selector(caseInsensitiveCompare:)];
NSMutableArray *pushArr = [[[NSMutableArray alloc] init] autorelease];
NSString *val = [[NSString alloc] init];
for(NSString *str in keys) {
val = (NSString *)[[tmp objectForKey:str] objectForKey:#"innertext"];
[pushArr addObject:val];
}
[val release];
[keys release];
return [NSArray arrayWithArray:pushArr];
}
#end
What is wrong with the code? Also app is crashing for this line of code
application is crashing if i include this line
NSArray *bgColor = [[NSArray alloc] initWithArray:[xmlObject fetchImmediateChildrenValues:[del.navigationbarStyle objectForKey:#"backgroundcolor"]]];
If I remove it application runs smoothly.
I have several comments on your code. One of them is the immediate cause of your crash, but you need to fix at least one other issue too. The short answer is that you over release val and keys.
NSArray *bgColor = [[NSArray alloc] initWithArray:[xmlObject fetchImmediateChildrenValues:[del.navigationbarStyle objectForKey:#"backgroundcolor"]]];
You don't need to create a new array here, you can simply write the following:
NSArray *bgColor = [xmlObject fetchImmediateChildrenValues:[del.navigationbarStyle objectForKey:#"backgroundcolor"]];
if you do, you don't need the [bgColor release] further down.
NSArray *keys = [[NSArray alloc] initWithArray:[tmp allKeys]];
keys = [keys sortedArrayUsingSelector:#selector(caseInsensitiveCompare:)];
These two lines leak the first NSArray, you alloc it but you overwrite it straight away with the sorted version. In fact, you can simply write:
keys = [[tmp allKeys] sortedArrayUsingSelector:#selector(caseInsensitiveCompare:)];
Note that you do not own keys so you can get rid of the [keys release] line further down.
NSString *val = [[NSString alloc] init];
for(NSString *str in keys) {
val = (NSString *)[[tmp objectForKey:str] objectForKey:#"innertext"];
[pushArr addObject:val];
}
[val release];
This is the source of your immediate problem. You first alloc a new string. Then you immediately overwrite it on each iteration of your loop. So the allocated NSString leaks. You do not own the val returned by [[tmp objectForKey:str] objectForKey:#"innertext"]; on each iteration, so the release ov val after the loop should not be there.
On a side note, objectForKey: returns an id - the cast to NSString* is redundant. Most people leave it out.
[keys release];
Going back to the bit above where I told you that you were leaking your alloc'd keys? Well the new version of keys you overwrote it with you don't own. Therefore you must not release keys here.
return [NSArray arrayWithArray:pushArr];
This is fine. My preference would be for:
return [[pushArray copy] autorelease];
but it is just a matter of style. You could also just return pushArray, but pushArray is mutable and the caller may rely on the return value being immutable.
Test your code with NSZombieEnabled set... It should give you enough informations to fix your problem.