I am writing a simple program having two UITextFields and one UIButton, when I press UIButton, a method is called, the coding of that method is as below
-(void) saveData{
restaurant_name_save = restaurant_name_textfield.text;
amount_name_save = amount_textfield.text;
order = [[NSMutableArray alloc] initWithObject: restaurant_name_save, amount_name_save, nil];
NSLog(#"%#", order);
UIAlertView *alert=[[UIAlertView alloc]initWithTitle:#"warning" message:[NSString stringWithFormat:#"%d",[order count]] delegate:nil cancelButtonTitle:#"ok" otherButtonTitles:nil];
[alert show];
[alert release];
}
In NSLog, both UITextField data is showing properly, but in UIAlertView, it is ever showing 2, even when I change the data and again press button...
What should I do, I simply want to save the data for each time in NSMutableArray, as I press button,,
please help ....
I might be missing something here but
it says [order count]:
UIAlertView *alert=[[UIAlertView alloc]initWithTitle:#"warning"
message:[NSString stringWithFormat:#"%d",[order count]]
delegate:nil cancelButtonTitle:#"ok" otherButtonTitles:nil];
The output of [order count] will be 2 because there are two entries in the array.
I would assume to see the contents of the array you write
stringWithFormat:#"%#", order
You might have a typo. Try using -initWithObjects: instead:
order = [[NSMutableArray alloc] initWithObjects: restaurant_name_save, amount_name_save, nil];
On repeat runs, also make sure you release the member variable order and set it to nil, before doing another +alloc and -initWithObjects: call:
if (order) {
[order release], order = nil;
}
order = [[NSMutableArray alloc] initWithObjects: restaurant_name_save, amount_name_save, nil];
...
Better yet, don't repeatedly use +alloc and -initWithObjects: in this method, but outside this method (perhaps in the larger object's init method) create an NSMutableArray of capacity 2:
self.order = [[[NSMutableArray alloc] initWithCapacity:2] autorelease];
In the method that handles the button action, set the items at their respective indices:
[order replaceObjectAtIndex:0 withObject:restaurant_name_textfield.text];
[order replaceObjectAtIndex:1 withObject:amount_textfield.text];
use initWithObjects insted of initWithObject
and always two objects set in the array and you are printing the count of array thats why it always show 2.
all other code written by you is correct.
If I follow you correctly, you want to have one array in which you want to store restaurant_name_save and amount_name_save each time a button on your screen is pressed. (I'm assuming this button calls the saveData method?
In that case, reallocating your array will clear any objects in it first and then adds the two strings to it. You should declare your array as a class variable and just do
[order addObject:string1];
[order addObject:string2];
EDIT:
Are you allocating it though? Here's a simple way of doing it, you might need to modify it to suit your needs -
Header file:
#interface WelcomeScreen : UIViewController {
NSMutableArray *array;
}
#property (nonatomic, retain) NSMutableArray *array;
-(IBAction) saveData:(id) sender;
Source file
#synthesize array;
-(void) viewDidLoad {
array = [[NSMutableArray alloc] init];
}
// Make the UIButton's tap option point to this -
-(IBAction) saveData:(id) sender
{
[array addObject:string1];
[array addObject:string2];
// alert.
}
Related
I'm having problem understanding memory management when creating a dictionary with mutable arrays. I'm using the ios6 SDK with deployment target 5.1.
In the implementation of the class "Group" the method "namesAndEmails" builds an array "emails" that contains the emails addresses for Person objects with an email. If the Person object does not have an email the Person name is added to another array "namesWithNoEmail". The arrays are returned in a dictionary.
#import "Group.h"
#implementation Group
-(NSDictionary*) namesAndEmails {
NSMutableArray *emails = [[NSMutableArray alloc] initWithCapacity:0] ;
NSMutableArray *namesWithNoEmail = [[NSMutableArray alloc] initWithCapacity:0];
NSString *email;
NSString *name;
for (Person *p in allPersons) {
email = p.email;
name = p.name;
if ([email length]==0) {
[namesWithNoEmail addObject:name];
} else {
[emails addObject:email];
}
}
NSArray *keys = [NSArray arrayWithObjects:#"emails",#"names", nil];
NSArray *objects = [NSArray arrayWithObjects:emails, namesWithNoEmail, nil];
//[emails release];
//[namesWithNoEmail release];
return [NSDictionary dictionaryWithObjects:objects forKeys:keys];
}
Somewhere else in the code I wish to send an email to a group of people so I call the emailGroup method which gets a dictionary out by calling "namesAndEmails" on the group.
-(void) emailGroup:(Group*) g {
NSDictionary *emailInfo = [g namesAndEmails];
guestsWithNoEmail = [emailInfo objectForKey:#"names"];
guestEmails = [emailInfo objectForKey:#"emails"];
int nGuestsWithNoEmail = [guestsWithNoEmail count];
if (nGuestsWithNoEmail > 0) {
UIAlertView *alert = [[[UIAlertView alloc] initWithTitle:#"No emails" message:#"" delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil] autorelease];
[alert show];
}
// some more code here
MFMailComposeViewController *picker = [[MFMailComposeViewController alloc] init];
picker.mailComposeDelegate = self;
[picker setSubject:subject];
[picker setMessageBody:#"" isHTML:NO];
[picker setToRecipients:guestEmails];
[[self delegate ] presentModalViewController:picker animated:YES];
[picker release];
}
As far as I understand [NSDictionary dictionaryWithObjects:objects forKeys:keys] in "namesAndEmails" returns an autoreleased dictionary. But why does my code crash if I release the "emails" and "namesWithNoEmail" arrays? I thought that the dictionary would have ownership of the array after they are added and therefore it would be safe to release the arrays in the method. I guess that's not correct, but why?
Is the a more clean way of doing this? Thank you for any advice!
My first suggestion would be to use the "Product->Analyze" feature. If you leaking or over releasing somewhere, it will probably give you the exact chain of events.
Secondly, I can't see the linking between your methods nameAndEmails and emailGroup:. Because I can't see the connection, I can't tell you if the autorelease is causing the problem.
Autoreleased objects get released when the the main run loop cycles. So it's very possible your NSDictionary is getting released. You could test this by doing anything from setting the memory location as a "watch" in the debugger to putting printing something in the console lines each time the runloop your in cycles (I made the assumption your in the main run loop, so correct me if that's not true).
Other things you can do to track the problem would be to use "Zombies" in instruments or NSZombieEnable=YES in your configuration
I am trying to generate an UIActionSheet from a NSDictionary.
codeCountryMap is NSDictionary variable defined in .h file. The code compiled correctly but crashes on run time. But the whole code works when the initialization is done in the handleEvents method
- (void)viewDidLoad {
[super viewDidLoad];
NSArray *codes = [NSArray arrayWithObjects:#"91", #"01", #"002", nil];
NSArray *cName = [NSArray arrayWithObjects:#"ABC", #"QWE", #"XYZ", nil];
codeCountryMap = [NSDictionary dictionaryWithObjects:codes forKeys:cName];
}
-(IBAction) handleEvents:(id)sender
{
UIActionSheet *displayCodeCountryMap = [[UIActionSheet alloc] initWithTitle:#"Select Country" delegate:self cancelButtonTitle:nil
destructiveButtonTitle:nil otherButtonTitles:nil,nil];
for(id key in codeCountryMap) {
[displayCodeCountryMap addButtonWithTitle:(NSString *)key];
}
[displayCodeCountryMap addButtonWithTitle:#"Cancel"];
displayCodeCountryMap.cancelButtonIndex = [codeCountryMap count];
displayCodeCountryMap.actionSheetStyle = UIActionSheetStyleBlackOpaque;
[displayCodeCountryMap showInView:self.view];
[displayCodeCountryMap release];
[country resignFirstResponder];
}
The app crashes when the handleEvents: method is called.
Any kind of help would be greatly appriciated.
Thanks in advance.
This more than likely is a memory management issue. It looks like you are setting the iVar codeCountryMap to an autoreleased object of NSDictionary and not retaining it(at least not in this code snippet). This means that when the 'handleEvents' method is called the NSDictionary item has been released from memory.
Try either retaining the NSDictionary item or, if codeCountryMap is defined as a retained property, then use the following code to set it.
self.codeContryMap = [NSDictionary dictionaryWithObjects:codes forKeys:cName];
This will use the synthesized methods to set the property and retain it.
No need for 2 nils terminators in the otherButtonTitles argument. If you had passed in strings then it would have to be nil terminated. Since you did not, one nil is enough.
UIActionSheet *displayCodeCountryMap = [[UIActionSheet alloc] initWithTitle:#"Select Country" delegate:self cancelButtonTitle:nil
destructiveButtonTitle:nil otherButtonTitles:nil];
Members of my favorite forum, I am stuck with a simple problem (chalk it up to the fact that I am fairly new)...
I am using an action sheet. I don't know how many buttons to display, so I decided to set the action sheet delegate to display FOUR buttons in total using string variables, knowing that if I only need to display 2, I can set the third variable to NIL and achieve that objective.
Example:
NSString *firstVehicle = [[NSString alloc] initWithString:[myVehicleList objectAtIndex:0]];
NSString *secondVehicle = [[NSString alloc] initWithString:[myVehicleList objectAtIndex:1]];
NSString *thirdVehicle = [[NSString alloc] initWithString:[myVehicleList objectAtIndex:2]];
NSString *fourthVehicle = [[NSString alloc] initWithString:[myVehicleList objectAtIndex:3]];
UIActionSheet *actionSheet = [[UIActionSheet alloc] initWithTitle:#"Please select a vehicle:" delegate:self cancelButtonTitle:#"Cancel" destructiveButtonTitle:nil otherButtonTitles:firstVehicle, secondVehicle, thirdVehicle, fourthVehicle, nil];
NSLog(#"firstOtherButtonIndex in the List is %i", [actionSheet firstOtherButtonIndex]);
[actionSheet showInView:self.view];
[actionSheet release];
break;
With that said, I don't always know that i will have four strings to create...when I pull back the myVehicleList array, I can use the count method to determine how many variables I have.
Question: So how does a smart programmer actually solve this problem? Meaning, how do I cycle through a for statement perhaps using the count returned when I check the array to only establish a certain number of strings and set the next string to nil?
Does that make sense? I can do IF statements for days, right. If count = 4, then set all four strings. But if count is 3, I set three strings to the array object values and the fourth to nil. But if the count is 2, I set two strings to the array object values and the third to nil, and so on.
There has to be a more effective way to dynamically create strings? I guess I could use fast enumeration, but how do I dynamically create a unique string object...
I hope I make some sense.
thanks!
NSMutableArray *vehicles = [[NSMutableArray alloc] init];
[vehicles addObject:#"One"];
[vehicles addObject:#"Two"];
[vehicles addObject:#"Three"];
[vehicles addObject:#"..."];
[vehicles addObject:#"Ten"];
UIActionSheet *actionSheet = [[UIActionSheet alloc] initWithTitle:#"Please select a vehicle:" delegate:self cancelButtonTitle:#"Cancel" destructiveButtonTitle:nil otherButtonTitles:nil];
for (NSString *vehicle in vehicles) {
[actionSheet addButtonWithTitle:vehicle];
}
[actionSheet showInView:self.view];
[actionSheet release];
I will prefer using an array of strings.
The real problem you're up against is that there's no good way to convert between a dynamic array and a varargs parameter (that is, one where you can pass any number of variables as arguments). If you wanted to do it the looping way and still use the varargs parameter, the best approach would be to create an array like NSString *strings[4] = {nil}, loop through it and then pass strings[0], strings[1], strings[2], strings[3] as arguments.
But I wouldn't do that. It's really duct-tapey. I would create the sheet with only the cancel button, then loop through an NSArray of strings that you want to use as titles and [sheet addButtonWithTitle:title].
I am trying to clean up my code from memory leaks and I am having problems with the 'release' method.
Here is my code:
NSArray *dict = [[NSArray alloc] initWithContentsOfURL:url];
if (dict == nil) {
UIAlertView *alert = [[UIAlertView alloc]
initWithTitle:#"Error"
message:#"Cannot retrieve content. Please try again later."
delegate:self
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alert show];
[alert release];
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
return;
}
self.schedule = dict;
[dict release];
[url release]; //I receive a runtime error here, "BAD ACCESS"
I don't understand why when I don't get the same problem with the line above [dict release];
Since you didn't post the code showing how URL was created, here's a general rule to follow:
If create the object with a initializer that starts with "init", then you should probably release it. If it's created another way (convenience method), then it's autoreleased. For example:
NSArray *a = [[NSArray alloc]initWithContentsOfURL:url]; // release this later
NSArray *a = [NSArray arrayWithContentsOfURL:url]; // this will be auto released
Basically you just need to look at whether the framework gave you an autoreleased object or not, because you can't release an autoreleased object or you'll (obviously) get a crash.
Take a look at the Memory Management Guide. It should be required reading.
You are responsible for calling release eat time, you call either alloc, copy, or retain.
In this case you called alloc on dict, but (presumably, although it is not shown where url comes from) not on url.
Objective-C allows you to send messages (e.g. 'release') to nil pointers without consequence.
If the pointer is non-nil and points to something bogus (i.e. an object that's been released), you'll get an EXC_BAD_ACCESS exception. Where does the url parameter come from and what is its retain count ([url retainCount]) before you call release?
I am assigning the contents of the clipboard to the UITextView text property. However, when I check the hasText property, the condition is always false.
NSString paste_text = [[NSString alloc] init];
self.paste_text = [UIPasteboard generalPasteboard].string;
....
my_UITextView.text = self.paste_text;
//THIS CONDITION IS ALWAYS FALSE
if (my_UITextView hasText)
{ UIAlertView *alert = [[UIAlertView alloc] initWithTitle:
#"Text ready to copy"
message:err_msg
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alert show];
}
You need to send your UITextView the hasText message by using brackets:
if ([my_UITextView hasText])
UPDATE:
Do you know that your UTTextView has text? You might want to check it on the console:
my_UITextView.text = self.paste_text;
NSLog(#"my_UITextView.text = %#",my_UITextView.text); // check for text
//THIS CONDITION IS ALWAYS FALSE
if ([my_UITextView hasText])
First off, your paste_text isn't used as intended since right after you alloc, it will immediately be discarded, possibly released, maybe not. You could actually just do away with the [[NSString alloc] init];.
Then add the following to test your code:
// delete:
// NSString paste_text = [[NSString alloc] init];
NSLog([UIPasteboard generalPasteboard].string);
self.paste_text = [UIPasteboard generalPasteboard].string;
NSLog(self.paste_text);
....
my_UITextView.text = self.paste_text;
NSLog(#"my_UITextView is %#, text contained: %#, my_UITextView , my_UITextView.text);
The first NSLog prints out the pasteboard string, the second the string once it is passed on to your paste_text, and the last will let you know if my_UITextView is non-nil, and what text it contains.
Also, if paste_text is a #property, what are its attributes? The text from [UIPasteboard generalPasteboard].string needs to be copied into it, otherwise when the pasteboard's string is changed, so is your paste_text.
How are you creating the my_UITextView property? If you did it in InterfaceBuilder, it's possible that you forgot to create an IBOutlet.