Storing and retrieving CGPoints inside NSMutableArray - iphone

I've looked through countless questions on here and elsewhere and cannot for the life of me figure out what I'm doing wrong.
I'm trying to store an array of CGPoints as NSValues inside of an NSMutableArray named points like so on the iPhone:
NSValue *point = [NSValue valueWithCGPoint:firstTouch];
NSLog(#"NSValue *point = %#", point);
[points addObject:point];
NSLOG OUTPUT
NSValue *point = NSPoint: {120, 221}
Everything is going smooth converting from the CGPoint to NSValue. But when I try to retrieve the point I get nothing.
NSValue *getPoint = [points objectAtIndex:0];
CGPoint thePoint = [getPoint CGPointValue];
NSLog(#"Point = %#", NSStringFromCGPoint(thePoint));
NSLOG OUTPUT
Point = {0, 0}
The points should be the same but I'm getting a null result.
For testing purposes this is happening in the touchesBegan method.
Does anyone have any idea where I'm going wrong? Thanks in advance.

I never allocated my array into memory and initialized it. My points weren't being stored into an array because there was no array that existed.

for adding CGPoint to NSMutableArray
[ArrObj addObject:NSStringFromCGPoint(PointObj)];
for getting CGPoint from NSMutableArray
CGPoint pointObj2 = CGPointFromString([ArrObj objectAtIndex:index]);
may this help you..

First allocate your mutable array in memory:
points = [[NSMutableArray alloc] init];
Then insert.

Related

Only the last value of NSMutableDictionary is added to NSMutableArray

I have tried my best to search for the answers to add NSMutableDictionary to NSMutableArray. Unfortunately, they all asked me to use "[allValues]" which is not what I want.
Here is the issue,
In the header file, I defined,
NSMutableArray *arData;
And in the m file,
arData = [[NSMutableArray alloc] init];
NSMutableDictionary *mutableDictionary5 = [[NSMutableDictionary alloc]init];
for (i=0;i<[[[self rssParser]rssItems]count];i++)
{
Lat = [[[[[self rssParser]rssItems]objectAtIndex:i]lat] doubleValue];
Lng = [[[[[self rssParser]rssItems]objectAtIndex:i]lng] doubleValue];
// Creates a marker in the center of the map.
GMSMarker *marker = [[GMSMarker alloc] init];
marker.position = CLLocationCoordinate2DMake(Lat, Lng);
marker.title = [[[[self rssParser]rssItems]objectAtIndex:i]header];
[mutableDictionary5 setObject:marker.title forKey:#"title"];
[arData setValue:[mutableDictionary5 valueForKey:#"title"] forKey:#"title"];
}
The "arData" only gets the last value of [[[[self rssParser]rssItems]objectAtIndex:i]header].And I am sure mutableDictionary5 gets all the value since I have put the following at the end of the code,
NSLog(#"mutableDictionary5 values=%#",[mutableDictionary5 allValues]);
It prints all the value of the title.
I understand it shouldn't be setvalue, however, I tried to use [mutableDictionary5 allValues], "addobjects", "allkeys". They are not working. What should I do to let the NSMutableArray arData to add all values of title with "title" as the key? thanks.
Sorry for my bad English which is not my native language.
You erroneously use setValue instead of addObject.
[arData addObject:mutableDictionary5];
This will work if you alloc init a new dictionary inside the loop.
If you want to reduce the array to just titles do this after the loop:
NSArray *titleArray = [arData valueForKeyPath:#"title"];
Now you have an array containing just titles.
BTW, you could make some effort to invent better variable names. Note that by convention in Objective-C variable names start with low cap while class names are capitalized.

Memory leak when using CLLocationCoordinate2D

I am using a NSMutableArray to hold CLLocationCoordinate2D values. After testing for a few mins I found the app crashing. I found heavy memory leaks associated with both the array and CLLocationCoordinate2D values.
Here is the code:
NSMutableArray *arrayReturn = [[NSMutableArray alloc] init];
CLLocationCoordinate2D obj1 = CLLocationCoordinate2DMake(37.6085289,107.5941445);
CLLocationCoordinate2D obj2 = CLLocationCoordinate2DMake(27.1727738,78.041655);
[arrayReturn addObject:[NSValue valueWithBytes:&obj1 objCType:#encode(CLLocationCoordinate2D)]];
[arrayReturn addObject:[NSValue valueWithBytes:&obj2 objCType:#encode(CLLocationCoordinate2D)]];
return [arrayReturn autorelease];
I am creating several objects like this and adding it to the array. Even though I have tagged an autorelease at the end, I have heavy memory leaks in the array.
What am I doing wrong here?
A CLLocationCoordinate2D is a simple struct of 2 double values and not an NSObject!
You allocate the CLLocationCoordinate2D on the stack and pass pointers to that data in the NSValue objects. But the stack memory will be not available after the method returns. This is why your app crashes.
You need to convert the CLLocationCoordinate2D into NSObjects somehow.
For example like this:
NSArray* coords = [NSArray arrayWithObjects:[NSNumber numberWithDouble:obj1.latitude], [NSNumber numberWithDouble:obj1.longitude], nil];

CGpoint Array write into a text file

I have an array like this: CGPoint array[1000] (I add location of touches into this array). The problem is that I want to save this array into a text file in the document directory and also retrieve it. Can anyone help with this?
Thanks All
Instead of using a plain C array, I would recommend that you use an NSArray to store your collection of CGPoints. Since NSArray conforms to the NSCoding protocol, you can serialize and deserialize it from a file.
You should read up on Archives and Serializations Programming Guide.
EDIT Here's an example:
// Create an NSMutableArray
NSMutableArray *points = [[NSMutableArray alloc] init];
// Add a point to the array
// Read up on NSValue objects to understand why you simply cannot add a C
// struct like CGPoint to the array
[points addObject:[NSValue valueWithBytes:&point1 objCType:#encode(CGPoint)]];
// archive the array -- this will store the array in a file
BOOL result = [NSKeyedArchiver archiveRootObject:points toFile:filePath];
NSLog(#"Archival result: %d", result);
// unarchive the array -- this will retrieve your array from the file
NSMutableArray *points2 = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath];

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?

Creating Objects & Setting iVars in a Loop?

NSArray *planetArray = [NSArray arrayWithObjects:#"Earth",
#"Jupiter",
#"Saturn",
#"Neptune",
#"Pluto", nil];
NSMutableArray *objectArray = [[NSMutableArray alloc] init];
for(NSString *eachPlanet in planetArray) {
Planet *newPlanet = [[Planet alloc] init];
[newPlanet setValue:eachPlanet forKey:#"name"];
[newPlanet setValue:#"TEST" forKey:#"type"];
[newPlanet setValue:[NSNumber numberWithInt:1234] forKey:#"mass"];
[objectArray addObject:newPlanet];
[newPlanet release];
}
for(Planet *displayEachPlanet in objectArray) {
NSLog(#"DATA: %#", displayEachPlanet);
}
[objectArray release];
I am curious if this is the best way to create an object and set an iVar for each item in an array. Basically I am:
Creating a Planet object
Setting the iVar (from the NSString array)
Adding the Planet object to an array.
Releasing the Planet object
Printing my Planet objects
Releasing the array
NB: I am just testing, this is not for anything, I was just curious ...
cheers Gary
Can't see anything drastically wrong about doing it that way. One suggestion would be to have an extended initialiser for your planet class, along the lines of:
-(Planet*) initWithName:(NSString*)name andType:(NSString*)type withMass:(int)mass;
And then create the planet with:
Planet *newPlanet = [[Planet alloc] initWithName:eachPlanet andType:#"Test" withMass:42];
Looks good to me. If all you are doing with the objects is printing something from them, you could probably do it in one loop with less initializing and such, but if thats just a test..it looks fine.