loading file content to NSArray - iphone

I'm using this code to load content to NSArray and it seem to work fine however Instrument to detect leaks point that there is a problem that I can't put my finger on:
- (void) loadPlan: (NSString *) fName
{
short j1;
fName= [NSString stringWithFormat:#"/%#", fName];
[self NewCase];
NSArray *arrayPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *docDirectory = [arrayPaths objectAtIndex:0];
NSString *filePath = [docDirectory stringByAppendingString:fName];
BOOL fileExists = [[NSFileManager defaultManager] fileExistsAtPath:filePath];
if (!fileExists) return;
NSString *fileContents = [NSString stringWithContentsOfFile:filePath
encoding:NSUTF8StringEncoding error:nil];
NSArray *chunks = [fileContents componentsSeparatedByString: #"#"];
for (i = 0; i <= 100; i++)
{
InputV[i] = [[chunks objectAtIndex: i+5] doubleValue];
}
...
for (j1 = 0; j1 <= 10; j1++)
{
GroupMode[j1] = [[chunks objectAtIndex: 206+j1] retain];
}
...
}
and on a init method someplace i have:
for (j1 = 0; j1 <= 10; j1++)
{
GroupMode[j1] = [[NSString alloc] initWithFormat:#""];
}
Instrument points to the NSAraay *chunks line code but i'm not sure what's the issue. Do i need to release it at some point ?
I appreciate any help.

In the comments you mention being able to call release. Therefore you are not using ARC and since I noticed you tagged with iphone you are not using GC. This leaves manual memory management.
The problem seems to be that either the chunks array or some chunks themseves are overretained (or underreleased). You are not showing all the code, so it's hard to say.
Make sure you do not retain either of them somewhere else in the code you did not show. Maybe show us the rest of the loadPlan method implementation.
edit: Now that you added more code, I can also expand this answer.
Answer this question: where is the retain call to the chunks matched with a release?
Also what is the declaration of GroupMode? It seems to be just an array of pointers. If so you probably need to release the old value before setting the new one.

Let me try another answer based on what you posted.
I'm assuming GroupMode is an instance variable of some class and is declared like this:
NSString* GroupMode[11];
The second loop in loadPlan should be:
for (j1 = 0; j1 <= 10; j1++)
{
NSString* aChunk = [chunks objectAtIndex: 206+j1];
if ( GroupMode[j1] != aChunk ) {
[GroupMode[j1] release];
GroupMode[j1] = [aChunk retain];
}
}
You should do something similar every time you change an element of GroupMode and you should make sure you release all GroupMode held objects in the dealloc method of that class.
I however suggest you do not use plain arrays and instead switch to using NSArray and/or NSMutableArray.

Take a look at this answer:
"componentsSeparatedByString" Memory leak
The problem is probably that something using the results is over-retaining the stuff from chunks. Instruments is pointing at this line because it's where the memory was first allocated, but it may not be the source of your problem.

Related

Objective-c Array Learning

Okay I am learning about arrays and how to work with them like I used to...... (Used to do alot of scripting but now am trying to learn to develop ipad and iphone app's
But my issue is I have it where it pulls a bunch of data from yahoo finance with a for loop..
But now my issue is how can I work with just one peice of the array data that has been pulled
here is my example
-(IBAction) clicked:(id)sender {
NSString * StockOneYahooFinance = [NSString stringWithFormat:#"http://finance.yahoo.com/q/hp?s=S+Historical+Prices"];
NSString * PulledStockOne = [NSString stringWithContentsOfURL:[NSURL URLWithString:StockOneYahooFinance] encoding:1 error:nil];
for (i=1;i<=10;i++){
NSString *StartPulling = [[PulledStockOne componentsSeparatedByString:#"nowrap align="] objectAtIndex:i];
NSString *StartOpen = [[StartPulling componentsSeparatedByString:#">"] objectAtIndex:3];
NSString *Open = [[StartOpen componentsSeparatedByString:#"<"] objectAtIndex:0];
NSString *StartClose = [[StartPulling componentsSeparatedByString:#">"] objectAtIndex:9];
NSString *Close = [[StartClose componentsSeparatedByString:#"<"] objectAtIndex:0];
NSMutableArray *StockOpens = [[NSMutableArray alloc] initWithCapacity:6];
[StockOpens addObject:Open];
sixtyday.text = [OpenValues objectAtIndex:10];
nintyday.text = [CloseValues objectAtIndex:10];
if ([OpenValues objectAtIndex:10]=[OpenValues objectAtIndex:11] {
sevenday.text = #"Plus One";
}
}
}
But now I want to do something like
year.text=StockOpens[5];
How can I do this.
Starting in Xcode 4.4 (LLVM 4.0), literals can be used for C-style subscripting in Objective-C.
year.text = StockOpens[5];
LLVM has documented the use of literal here: Objective-C Literals
Note: Because Clang will translate the literal usage, in this case to objectAtIndexedSubscript:, the OS X v10.8 (or iOS 6) Foundation framework is required.
StockOpens is an array object so you need to call a method to get the object at an index. On NSMutableArray its [StockOpens ObjectAtIndex:5]
year.text = [[StockOpens objectAtIndex:5]StringValue];
To do StockOpens[5] you need to use a C-array.
This depends on what kind/class of objects you fill StockOpens with, if it's simply NSStrings, you can do
year.text = [StockOpens objectAtIndex:5];
If it's some other object that is not a string, you can maybe call its description:
year.text = [[StockOpens objectAtIndex:5] description];
P. s.: there's documentation on developer.apple.com, please read it! This question is so simple (and fundamental) that it must not be asked on SO.

GData Objective C client memory leak

I have a method where I fetch GDataFeedBase entries and return these as an array to another function
NSMutableArray *tempFeedArray = [NSMutableArray array];
NSURL *feedURL = [[NSURL alloc] initWithString:escapedUrlString];
NSData *data = [NSData dataWithContentsOfURL:feedURL];
GDataFeedBase *feedBase = [[GDataFeedBase alloc] initWithData:data];
[tempFeedArray addObjectsFromArray:[feedBase entries]];
[feedURL release];
[feedBase release];
return tempFeedArray;
.....
I have another function where I retrieve required values from tempFeedArray object that is GDataEntryYouTubeVideo
for(int count = 0; count < loopCount; count ++){
NSMutableDictionary *feedBaseEntryDict = [[NSMutableDictionary alloc] init];
entry = [tempFeedArray objectAtIndex:count];
youTubeUrl = [[entry alternateLink] href];
if ([entry statistics]!= nil) {
noOfVws= [[[entry statistics] viewCount] intValue];
}
duratn = [[[entry mediaGroup] duration] stringValue];
descr = [[[entry mediaGroup] mediaDescription] stringValue];
authorName = [[[entry authors] objectAtIndex:0] name];
publishedDt = [[entry publishedDate] stringValue];
rating = [[[entry rating] average] stringValue];
imageURL = [[[[entry mediaGroup] mediaThumbnails] objectAtIndex:0] URLString];
videoTitle = [[[entry mediaGroup] mediaTitle] stringValue];
.....
}
......
For the first time everything works fine. But the next time, it shows memory leak at
GDataXMLNode stringFromXMLString:
Did anyone else face this issue?
I found similar issue raised in gdata developer forum:
http://groups.google.com/group/gdata-objectivec-client/browse_thread/thread/f88de5a7bb784719/cab328a8725ee6c5
but the solution doesn't solve the issue.
Any help is much appreciated.
Looks like it might not be your code but the client library there were a few other threads on the same issue. This one has a work around, but I have not tried it myself.
The other options you have would be to upgrade to latest version (1.12 was released on Apr 11th 2011), take a look at the source and try to track down your problem, or submit an issue (it looks like the project is still actively developed).
Since the code is "stealing" entries from the feed, leaving them pointing to their parent feed (rather than copying the entries, which would create independent versions) there may be an issue with the strings cache. Try disabling the cache by commenting out -addStringsCacheToDoc in GDataXMLNode.m

Class initialization breaking application

I've recently created a new class for my iPhone application which will hold information read from a text file containing the street address and GPS points of points of interest.
The issue though is that whenever I add code to initialize the class my application loads up and the instantly quits with no errors in the console. When I remove it, everything is fine. I simply cannot see anything wrong with the code.
Here is the constructor:
#import "GPSCoordinate.h"
#implementation GPSCoordinate
-(GPSCoordinate*) initWithData:(NSString *)rawData size:(int)size
{
self = [super init];
location = [NSMutableArray arrayWithCapacity:size];
coordinates = [NSMutableArray arrayWithCapacity:(int)size];
NSArray *tokens = [rawData componentsSeparatedByString:#"#"];
for (int i = 0; i < size - 1; i++) {
//Sub tokens
NSString *line = [tokens objectAtIndex:i];
NSArray *lineTokens = [line componentsSeparatedByString:#":"];
//Store address
[location addObject:[lineTokens objectAtIndex:0]];
//Store GPS coords
NSString *coords = [lineTokens objectAtIndex:1];
coords = [[coords stringByReplacingCharactersInRange:NSMakeRange(0, 1) withString:#""]
stringByReplacingCharactersInRange:NSMakeRange([coords length]-2, 1) withString:#""];
NSArray *coordsTokens = [coords componentsSeparatedByString:#" "];
CLLocationCoordinate2D coord;
coord.latitude = [[coordsTokens objectAtIndex:0] doubleValue];
coord.longitude =[[coordsTokens objectAtIndex:1] doubleValue];
[coordinates addObject:coords];
[line release];
[lineTokens release];
[coords release];
[coordsTokens release];
}
return self;
}
#end
Here is the call I make to it in another class:
self.gps = [[GPSCoordinate alloc] initWithData:gpsRawData size:[[gpsRawData componentsSeparatedByString:#"#"] count]];
Where am I going wrong with this?
I see a number of problems.
You're not checking the return value of [super init].
You're storing autoreleased arrays in what are presumably ivars (location and coordinates).
You're passing a separate size parameter which is calculated from the rawData outside of the call, but -initWithData: makes the exact same calculation inside the method. The size: parameter seems completely superfluous here.
You're skipping the last token entirely. You should take that for loop and make the condition simply i < size. Alternately if you're targetting iOS 4.0 or above you can turn the entire loop into
[tokens enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop){
NSString *line = obj;
// rest of loop body
}];
Since you don't seem to need the index inside the loop, you could also just use a for-in loop (this will work on pre-4.0 iOS devices):
for (NSString *line in tokens) {
// body of loop
}
You're not checking that your data is valid. If a line contains "foo", your program will crash when it tries to access [lineTokens objectAtIndex:1]. Similarly it'll crash if you have the string "foo:" as it tries to remove the first character of the coordinates variable. In fact anything less than 2 characters after the colon will crash. It'll also crash if there's no spaces after the colon.
And finally, all those calls to -release at the end will crash. All 4 of those objects are autoreleased objects, so by calling -release on them now you're simply guaranteeing that the app will crash when the autorelease pool is drained.
You're also storing coords (e.g. the string) in your coordinates array. Presumably you meant to store coord, though you'll need to wrap it in an NSValue in order to store it in an NSArray.
I see several issues.
1) Most fundamentally, you are releasing a lot of objects that you didn't allocate. For example:
NSString *line = [tokens objectAtIndex:i];
....
[line release];
is incorrect. Review the Cocoa Memory Management Rules.
2) Why are you doing [[gpsRawData componentsSeparatedByString:#"#"] count to pass the size to
your initWithData:size: method, when you're just going to have to repeat the -componentsSeparatedByString: call inside your method. Passing a separate "size" doesn't gain you anything, involves a redundant parse of the input, and opens up more possible bugs (what if the caller passes in a "size" that doesn't match the number of "#"s in the input - you aren't handling that error condition).
3) I also see that you are assigning latitude/longitude to CLLocationCoordinate2D coord; but not doing anything with it. Is that deliberate?

How to persist data using archiving function on the iphone?

I am having trouble with persisting data.
Desired Behavior:
I have a text field defined in the view that captures user input. When the submit button is clicked (touch up inside), I expect to write that data to a mutable array. As of right now, I am going to archive that mutable array upon submit, but have also played with archiving once the view disappears/unloads. When a user navigates away from the view or exits the application and re-enters, I want to display the first element of that mutable array in the textfield.
Actual Behavior:
Textfield captures data and updates myMutableArray upon submitting. I have a few labels on the view plus NSLog to verify the count grows as I hit submit. I then archive this the first time, and I see that my file now exists in myDocuments plus when I revisit the view and check the count of dataArray, which is created if that file exists, the count of dataArray matches the number of elements I created in myMutableArray. If I start to enter in data again, it resets myMutableArray and dataArray all over again. That is one of a couple problems I think I have.
Questions:
If the file exists, then dataArray is created. Now I try to set the textfield to display the first element and the program bombs out! If dataArray exists, and I can see count is some positive value, I don't understand why I can't access that first element.
I feel like when I visit the view and go through this exercise, when I revisit the view I see I have dataArray present and with positive values, but when I start adding text again through the submit button, I reset everything! So I am persisting once maybe, but then wiping it clean. So much for real persistence. What am I doing wrong?
Eventually, I will be storing a record with a few elements. I will create multiple records over time for the user to review. Is archiving the best possible way? Should I start using NSCoding instead?
- (void)viewDidLoad {
[super viewDidLoad];
// Initialize mutable array and set equal to myMutableArray property
NSMutableArray *aMutableArray = [[NSMutableArray alloc] init];
myMutableArray = aMutableArray;
// Debug Logging
// NSLog(#"Mutable Array Count is: %i", [myMutableArray count]);
NSFileManager *filemgr;
NSString *docsDir;
NSArray *dirPaths;
filemgr = [NSFileManager defaultManager];
// Get the documents directory
dirPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
docsDir = [dirPaths objectAtIndex:0];
// Build the path to the data file
dataFilePath = [[NSString alloc] initWithString: [docsDir stringByAppendingPathComponent: #"data.archive"]];
// Check if the file already exists
if ([filemgr fileExistsAtPath: dataFilePath])
{
NSMutableArray *dataArray;
dataArray = [NSKeyedUnarchiver unarchiveObjectWithFile: dataFilePath];
myTextField.text = #"%#", [dataArray objectAtIndex:0]; // THIS BOMBS OUT MY PROGRAM FOR SURE IF INCLUDED!!!
//myUpdatedMutableArray = dataArray;
// Debug Logging
NSLog(#"Mutable Array (dataArray) Count is: %i", [dataArray count]);
//NSLog(#"The first value in the array is: %i", [dataArray objectAtIndex:0]);
}
[filemgr release];
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem;
}
- (IBAction) mySubmitButtonPressed:(id)sender {
// Debug Logging
NSLog(#"Submit Button was Pressed!");
NSLog(#"Mutable Array (myMutableArray) Count is: %i", [myMutableArray count]);
// Create string from textfield and addobject to myMutableArray; check to see that myMutableArray grows in count
NSString *myString = [[NSString alloc] initWithFormat:#"%#",myTextField.text];
[myMutableArray addObject:myString];
NSLog(#"Mutable Array (myMutableArray) Count is: %i", [myMutableArray count]);
// More Debug Logging just using on-screen labels
NSString *mySecondString = [[NSString alloc] initWithFormat:#"%i", [myMutableArray count]];
myFirstLabel.text = myString;
mySecondLabel.text = mySecondString;
// Archive myMutableArray
[NSKeyedArchiver archiveRootObject: myMutableArray toFile:dataFilePath];
//[contactArray release];
}
There are many issues with this code.
To begin, you leak memory all over the place. Specifically here:
NSMutableArray *aMutableArray = [[NSMutableArray alloc] init];
myMutableArray = aMutableArray;
Which should really be:
self.myMutableArray = [NSMutableArray array];
After you unarchive the data, if you want to keep it in myMutableArray, simply do this:
[myMutableArray appendArray: dataArray];
Or just:
self.myMutableArray = dataArray;
You should never release singleton instances like the NSFileManager, so completely get rid of this one:
[fileMgr release];
Your code crashes because this is not valid code:
myTextField.text = #"%#", [dataArray objectAtIndex:0];
(It actually is valid code, but it will do something very different than you think)
This is not Python, so you will have to do this:
myTextField.text = [NSString stringWithFormat: #"%#",
[dataArry objectAtIndex: 0]];
But that is really the same as:
myTextField.text = [dataArray objectAtIndex: 0];
Adding a string to your array in mySubmitButtonPressed can be done much simpler than you think. Just do this:
[myMutableArray addObject: myTextField.text];
You should really learn about autoreleased objects, memory management and what retaining properties actually do.

iphone Development - GDB error signal EXC_BAD_ACCESS

i get the signal error EXC_BAD_ACCESS when trying to retrieve the return output from the randomBallPick method, i probably do it wrong.
NSString *temp = [self randomBallPick];
upBall1.image = [UIImage imageNamed:temp];
Array (containers) retain/release items that are added/removed.
The object will receive release when it's removed from container with removeObjectAtIndex: so you need to retain it before it is removed and possibly autorelease since you are returning it from your method.
NSString * chosenFilename =
[[[imageArray objectAtIndex:chosen] retain] autorelease];
[imageArray removeObjectAtIndex:chosen];
return chosenFilename;
OK, can you try this, please?
NSString *chosenFilename = [[imageArray objectAtIndex:chosen] retain];
[imageArray removeObjectAtIndex:chosen];
return [chosenFilename autorelease];
All you need to do is:
NSString *chosenFilename = [[imageArray objectAtIndex:chosen] retain];
Since the objectAtIndex method returns an autorelease object.
There are several things wrong with this piece of code.
You are initializing your array of names once, but then you keep removing stuff from it... I'm not sure you want to do this, otherwise you'll start returning nil exclusively (after the 38th call...). You may want to re-fill your array in that case. Here's a better version of your routine (I think):
static NSMutableArray *imageArray = nil;
if (!imageArray.count) {
if (imageArray==nil) imageArray = [[NSMutableArray alloc] init];
for (int c = 0; c < 37; c++)
{
NSString *imageName = [NSString stringWithFormat:#"ball_%i.png", c];
[imageArray addObject:imageName];
}
}
// Now we are basically sure that imageArray.count > 0
assert(imageArray.count>0);
NSUInteger chosen = arc4random() % imageArray.count;
NSString *chosenFilename = [[imageArray objectAtIndex:chosen] retain];
[imageArray removeObjectAtIndex:chosen];
return [chosenFilename autorelease];
As others have said, you have to retain then autorelease the strings you extract from the array (because the array releases them upon removal).
Note also that you should call 'retain' on the string before removing it from the array. The string is already released after the removeObjectAtIndex: call... so it's already too late to retain it then.
As soon as you remove the object from the array, its retain count is probably zero, and it will get dealloced. Try doing
return [[chosenFilename] retain] autorelease]