initializing memory for an NSMutableArray / design pattern / refactoring [closed] - iphone

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 8 years ago.
Improve this question
I have a view that displays in a UIPopoverController. Before, it just had properties that were associated with it's view for a single object. So it would be something like
TargetDetailView : NSObject
#property Target *target
- (id)initWithTarget:(Target *)target
Now however, in some situations depending on what level zoom the user is looking at our view, there will be multiple targets in the popover. So I added an NSMutableArray *targets property to the class in order to show multiple targets in the popover.
Right now, the actual target objects that get drawn on our view are getting drawn on top of each other which is a waste.
The initWithTarget method only got called when you actually clicked on a target and the popover got presented.
What I'm trying to do now is calculate ahead of time where I am goign to have multiple targets in the same location, draw it only once, but when pressed have the popover show and this time, use the NSArray *targets to search through to show the multiple targets in that location.
The problem I have is I do not know where to initialize the memory for the NSMutableArray *targets. The TargetDetailView only gets initialized when you press the target, but I want to calculate ahead of time from my other class when the target views are on top of each other to only draw it once.
I sort my targets in order in terms of location, then I look to see if the first two are similar enough in location to bin them:
for (int i = 0; i < [sortedArray count] - 1; i++) {
TargetDetailView *firstTargetDetailView = (TargetDetailView *)[sortedArray objectAtIndex:i];
TargetDetailView *secondTargetDetailView = (TargetDetailView *)[sortedArray objectAtIndex:i + 1];
if (secondTargetDetailView.target.location - firstTargetDetailView.target.location < scale) {
[firstTargetDetailView.targets addObject:secondTargetDetailView.target]; // crash
[newTargetDetailViewArray addObject:(TargetDetailView *)[sortedArray objectAtIndex:i]];
}
}
I try to have the first TargetDetailView.target property point to the next TargetDetailView if it's close enough in location. However, the .targets property never gets initialized since my TargetDetailView only gets initialized on press.
How should I either
a) allocate memory for this object
b) refactor and/or allocate memory for this object
Thanks.

If I may make a suggestion, you could use the same initWithTarget method but always keep your targets in an array. You could then initialize your array there and add your Target parameter from the start, like this:
#interface TargetDetailView :NSObject
#property (nonatomic, retain) NSMutableArray *targets;
- (id)initWithTarget:(Target *)target;
#end
#implementation TargetDetailView
#synthesize targets;
- (id)initWithTarget:(Target *)target {
self = [super init];
if (self) {
targets = [[NSMutableArray alloc] initWithObject:target];
}
return self;
}
You can then use the targets property to add more targets (if you'd like) and use a simple loop through targets without having to care about the number of items in there.

Related

Showing something different in a label every time the view is loaded

What I want to do, is when the app is loaded, the user see's a tip. I have about twenty of them and I want just a label to show them. I can show them in order, I just don't know how to show a different one every time. So is their a method to just go through an order every time the view is loaded?
so far i have done this
Made a tip lebel to be set
#property (weak, nonatomic) IBOutlet UILabel *tipLabel;
and im gunna set it in the view did load
#synthesize tipLabel;
- (void)viewDidLoad
{
[super viewDidLoad];
tipLabel.text = // What Do I put here to pick from my list of 20 strings?
(in order or random)
You don't say where or how you are storing your list (or array) of 20 strings, but assuming it is a "NSArray" object with 20 strings, you could do something like this:
tipLabel.text = [arrayOfTips objectAtIndex: (arc4random() % [arrayOfTips count])];

release an object, memory management, Objective C [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 8 years ago.
Improve this question
If I have
ClassA.h
1.#property (nonatomic, retain) NSMutableArray *arr;
ClassA.m
-(ClassA) function {
1.ClassA *obj = [[ClassA alloc] init];
// initialize the arr
// add another object into the arr
2. [obj.arr addObject:nameOfAnObject]
3. return obj;
}
at ClassB, I want to call (ClassA) function method.What I do is
ClassB.m
-(void)viewDidload {
1.[super viewDidLoad];
2.ClassA *classAinB = [[classA alloc] init];
3.classAinB = [classA function];
4.[classAinB release];
}
According the rule of memory management,because I own a ClassAinB in ClassB.m, so I free it at the end.
My question is how is the var ClassA which I own in ClassA.m, when should I release it so that after a call back at line 3 of ClassB, I still have the object of ClassA whose arr is containing nameOfAnObject object
Please advice me on this issue. Any comments are welcomed here. Thanks
Add it to the autorelease pool. i.e. return [obj autorelease];. This will make sure the object is sent a release message at the end of the run loop iteration if it no longer has any owner.
If you use manual retain-release:
You allocate 2 objects of classA. One - when you directly allocate it from classB function, another - when you calls classA function inside that function. So, when line 3 performs, you destroys link (overwrite classAinB var) on first classA object and this is a memory leak.
If you wish use factory method that returns you an object of classA, make that method static.
Anytime you have an alloc you need a corresponding release to match it.
In XCode 4.2 + with the LLMV compiler (and ARC enabled) you don't have to worry about reference counting, so when you alloc an object, you don't need to worry about calling release, in fact you can't, as the compiler will throw an error.

Creating a MKPolygon from user-placed annotations in map

I want the user to be able to create polygons after placing some (unknown number) MKpointAnnotations in the map.I have put a gesture recognizer that gets activated once the user taps a button, and so annotations are placed.But how to use these as corners for a MKPolygon?
Below the code for saving the corners of the polygon.This after some mods I did to it.Now the app crashes and the crash reporter says index out of range.The corners are MKPointAnnotation-s created via a GestureRecognizer.
-(IBAction)addCorner:(id)sender
{
NSMutableArray *addCorners = [[NSMutableArray alloc] init];
[addCorners addObject:pointAnnotation];
ptsArray = addCorners;
}
-(IBAction)addPolygonOverlay:(id)sender
{
int cornersNumber = sizeof(ptsArray);
MKMapPoint points[cornersNumber];
for (int i=0; i<cornersNumber; i++) {
points[i] = MKMapPointForCoordinate([[ptsArray objectAtIndex:i] coordinate]);
}
MKPolygon *polygon = [MKPolygon polygonWithPoints:points count:cornersNumber];
[mapview addOverlay:polygon];
}
First problem is the addCorner method. Instead of adding each corner to the ptsArray variable, it creates a new array with just the last corner and sets theptsArray equal to that so it only has the one, last corner.
Change the addCorner method like this:
-(IBAction)addCorner:(id)sender
{
if (ptsArray == nil)
{
self.ptsArray = [NSMutableArray array];
}
[ptsArray addObject:pointAnnotation];
}
Also make sure ptsArray is declared and synthesized properly:
//in the .h file...
#property (nonatomic, retain) NSMutableArray *ptsArray;
//in the .m file...
#synthesize ptsArray;
(By the way, why not add the corner to ptsArray right where the pointAnnotation is created instead of in a separate user action?)
Second problem is in the addPolygonOverlay method. You have to use the NSArray count property to get the number of items in the array. The sizeof function returns the number of bytes of physical memory the passed variable uses. For ptsArray which is a pointer, it will return 4. If the ptsArray has less than 4 items, you will get the "index out of range" exception.
So change
int cornersNumber = sizeof(ptsArray);
to
int cornersNumber = ptsArray.count;
Another important thing to note is that the polygon sides will be drawn in the order the points are in the array. If the user does not add corners in a clockwise or counter-clockwise order, the polygon will look strange. You could re-create the polygon overlay immediately after a user adds/removes an annotation so they get immediate feedback on how it will look.

creating a Mutable array that can be added to in later clicks of the same button?

General noob questions:
(1) How can I create an NSMutable array in a buttonClicked action that I can add more entries to during subsequent clicks of the same button? I always seem to start over with a new array at every click (the array prints with only 1 entry which is the most recent button's tag in an NSLog statement).
I have about 100 buttons (one for each character in my string called "list") generated by a for-loop earlier in my code, and each has been assigned a tag. They are in a scrollview within the view of my ViewController.
I wish to keep track of how many (and which ones) of the buttons have been clicked with the option of removing those entries if they are clicked a second time.
This is what I have so far:
-(void) buttonClicked:(UIButton *)sender
NSMutableArray * theseButtonsHaveBeenClicked = [[NSMutableArray alloc] initWithCapacity: list.length];
NSNumber *sendNum = [NSNumber numberWithInt:sender.tag];
[theseButtonsHaveBeenClicked addObject:sendNum at index:sender.tag];
NSLog(#"%#",theseButtonsHaveBeenClicked);
}
(2) I have read that I may be able to use a plist dictionary but I don't really understand how I would accomplish that in code since I cant type out the items in the dictionary manually (since I don't know which buttons the user will click). Would this be easier if I somehow loaded and replaced the dictionary in a plist file? And how would I do that?
(3) I also have no idea how I should memory manage this since I need to keep updating the array. autorelease?
Thanks for any help you can provide!
Okay, firstly you are creating a locally scoped array that is being re-initialised on every call to buttonClicked:. The variable should be part of the class init cycle.
You will also be better off with an NSMutableDictionary instead of an NSMutableArray. With a dictionary we don't have to specify capacity and we can use the button's tags as dictionary keys.
Here's what you need to do, these three steps always go together: property/synthesize/release. A good one to remember.
//Add property declaration to .h file
#property (nonatomic, retain) NSMutableDictionary * theseButtonsHaveBeenClicked;
//Add the synthesize directive to the top of .m file
#synthesize theseButtonsHaveBeenClicked;
// Add release call to the dealloc method at the bottom of .m file
- (void) dealloc {
self.theseButtonsHaveBeenClicked = nil; // syntactically equiv to [theseButtonsHaveBeenClicked release] but also nulls the pointer
[super dealloc];
}
Next we create a storage object when the class instance is initialised. Add this to your class's init or viewDidLoad method.
self.theseButtonsHaveBeenClicked = [[NSMutableDictionary alloc] dictionary]; // convenience method for creating a dictionary
And your updated buttonClicked: method should look more like this.
-(void) buttonClicked:(UIButton *)sender {
NSNumber *senderTagAsNum = [NSNumber numberWithInt:sender.tag];
NSString *senderTagAsString = [[NSString alloc] initWithFormat:#"%#",senderTagAsNum];
// this block adds to dict on first click, removes if already in dict
if(![self.theseButtonsHaveBeenClicked objectForKey:senderTagAsString]) {
[self.theseButtonsHaveBeenClicked setValue:senderTagAsNum forKey:senderTagAsString];
} else {
[self.theseButtonsHaveBeenClicked removeObjectForKey:senderTagAsString]; }
[senderTagAsString release];
NSLog(#"%#", self.theseButtonsHaveBeenClicked);
}

I'm having issues inputing a name if applicable

I have a function here that upon completing a single round, if your score is higher than either a default score entry or a newly placed high score then it will swap its data with your data and push everything else down. removing the last entry from the list. currently this is just one exchange and for functions sake I'm going to hard code it and then refactor it later.
My main problem is that when I set up a text input view to capture the players name execution continues immediately without the players input and crashes the game. I commented out the line that sets the text because I have a default value in place just in case any attempt that I try to make fails. How can I get Execution to wait for a moment while input is taken? Would I have to set up a delegate method? If so I'm still a bit confused by delegates. I could set it up to work but I don't understand it, so I wouldn't be able to do any other special custom tasks with it. I've worked on it for a while and got no further...
-(void)saveData:(ScoreKeep *)stats{
NSMutableDictionary *swap = [[NSMutableDictionary alloc]init];//used for swaping entries
NSString *filePath = [self pathOfFile];
NSLog(#"Writing to %#", filePath);
if ([[NSFileManager defaultManager]fileExistsAtPath:filePath]) {
NSLog(#"Loading previous dictionary to save...");
dataDictionary = [NSMutableDictionary dictionaryWithContentsOfFile:filePath];
if ([dataDictionary objectForKey:#"1"]) {
NSMutableDictionary *highScore = [dataDictionary objectForKey:#"1"];
if ([stats.score intValue] > [[highScore objectForKey:#"SCORE"] intValue]) {
NSLog(#"You Win! score: %# highscore: %#", stats.score,[NSNumber numberWithInt:[[highScore objectForKey:#"SCORE"] intValue]] );
stats = [[ScoreKeep alloc] initWithNibName:#"Scorekeep" bundle:nil];
NSLog(#"Setting up name entry");
[self.view addSubview:stats.view]; //New view is added so that the player can input data(Assume it is complete);
//stats.nameTag = setName.nameTag;//This line is executed before the new view is dismissed causing an error to occur
[stats setupDictionary]; // It just goes down hill from here if the previous line is uncommented
[dataDictionary setObject:stats.sComponents forKey:#"1"];
}else {
NSLog(#"You Lose: %# highscore: %#", stats.score,[NSNumber numberWithInt:[[highScore objectForKey:#"SCORE"] intValue]] );
}
NSLog(#"Got first place entry");
}else {
NSLog(#"Initilizing Score");
}
}else{
NSLog(#"Creating new dictionary to save...");
dataDictionary = [[NSMutableDictionary alloc]init];
}
[dataDictionary writeToFile:filePath atomically:YES];
}
Help would greatly be appreciated. If more information is needed I'd be happy to provide.
by the way ScoreKeep is an object that contains a dictionary and a function to create a dictionary such that it can set any values I need and package them into sComponents(the dictionary to be entered into the main savefile)
#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h>
#class omphalosUtility;
#pragma mark -
#pragma mark Saving data
#pragma mark -
static inline void poop(){
NSLog(#"POOP");
}
I'm going to try making a utility file that works independently of the app so that I Can update files and perform other universal operations such as saving when needed. Its a step in a direction that i'd like to take.
If i get it right, (The code is really nasty, man...) your problem is that you are trying to present a View Controller with the wrong way.
Correct me if i'm wrong, is ScoreKeep is a ViewController? if so, you have to name it properly. that's for a start.
Second, you cant present another view controller only by adding its "view" property to the current view controller's View Hierarchy. that way the view will not respond properly to the events.
the correct way to to what you'r trying to do is by presenting the ScoreKeep ViewController modally.
there is no other right way to do this without using delegation. you will have to acquire this technique.
Your view controller that responsible for getting the name from the user need to have a way to tell it's master view controller that the user entered a name. and that is achieved through delegation.
What you should do:
Basically you create a protocol called something like "NamePrompterViewControllerDelegate"
that will have at least one method that will be called when the user will done entering his name.
Your ScoreKeepViewController should have an instance variable that implemented the protocol (Look at the apple documentation on protocols for assistance)
Your main view controller (the one that contains the method you added) then should implement the protocol you created, and set itself as the delegate of ScoreKeep like that:
stats = [[ScoreKeep alloc] initWithNibName:#"Scorekeep" bundle:nil];
stats.delegate = self;
For more info on presenting and dismissing ViewControllers modally you should read the documentation at Apple Documentation
I hope i helped you, there is just a lot to cover and it hardly can be done by writing an answer.
Feel free to ask more for clearance.