Pointer not updating when passed into a method? - objective-c

The pointer passed into the "menuItem" methode is alloc and for the "Pointer Inside" a value is registered. But for the "Pointer Outside" the value is "null" ... why? I passed in the pointer and it should had been modified in the method?
In the header file:
UIButton *bMenu_time;
UILabel *lMenu_time;
Implementation:
- (void) menuItem: (UIView*)vMenu menuButton:(UIButton*)bMenu menuLabel: (UILabel*)lMenu menuPosX: (double)posX menuLenX: (double)lenX menuTagNum: (int)tagNum menuText: (NSString*)txtMenu{
bMenu = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[bMenu setFrame:CGRectMake(posX,0,lenX,25)];
[bMenu setTag: tagNum];
[bMenu addTarget:pSelf action:#selector(NewNumber:) forControlEvents:UIControlEventTouchUpInside];
[vMenu addSubview:bMenu];
lMenu = [[UILabel alloc] initWithFrame:CGRectMake(posX,0,lenX,25)];
[lMenu setBackgroundColor:[UIColor lightGrayColor]];
[lMenu setText:[NSString stringWithFormat: txtMenu]];
[lMenu setFont:[UIFont systemFontOfSize:14 ]];
[lMenu setTextAlignment:UITextAlignmentCenter];
[vMenu addSubview: lMenu];
NSLog(#"\nPointer Inside: %#\n", lMenu); // <--------- INSIDE WORKS
}
- (void) menuBuild{
pSelf = self;
theString = #"";
vMenu = [[UIView alloc] initWithFrame:CGRectMake(0,0,320,25)];
[pSelf.view addSubview:vMenu];
[vMenu setBackgroundColor:[UIColor grayColor]];
iTime = 2;
[self menuItem:vMenu menuButton:bMenu_time menuLabel:lMenu_time menuPosX:240+20 menuLenX:60 menuTagNum:102 menuText:[NSString stringWithFormat: #"Hold: %d", iTime]];
NSLog(#"\nPointer Outside: %#\n", lMenu_time); // <----- OUTSIDE is NULL ??
}

Objective C, like C, is pass by value. That means, if you want to change a pointer by passing it to a function, you need to pass a pointer to the pointer and use that.
In C, that would be something like:
void alloc128 (void **ptr) {
*ptr = malloc (128);
}
Mapping that to your specific case, you can:
modify menuBuild to pass the address of the things you want to change.
modify the function to receive the pointer-to-pointer values.
modify the function to dereference the pointer-to-point values to set them correctly.

paxdiablo is absolutely right, but in case you're uncertain of the syntax for pass-by-reference, here is what your code should be:
- (void) menuItem: (UIView*)vMenu menuButton:(UIButton **)bMenu menuLabel: (UILabel **)lMenu menuPosX: (double)posX menuLenX: (double)lenX menuTagNum: (int)tagNum menuText: (NSString*)txtMenu{
*bMenu = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[*bMenu setFrame:CGRectMake(posX,0,lenX,25)];
[*bMenu setTag: tagNum];
[*bMenu addTarget:pSelf action:#selector(NewNumber:) forControlEvents:UIControlEventTouchUpInside];
[vMenu addSubview:*bMenu];
*lMenu = [[UILabel alloc] initWithFrame:CGRectMake(posX,0,lenX,25)];
[*lMenu setBackgroundColor:[UIColor lightGrayColor]];
[*lMenu setText:[NSString stringWithFormat: txtMenu]];
[*lMenu setFont:[UIFont systemFontOfSize:14 ]];
[*lMenu setTextAlignment:UITextAlignmentCenter];
[vMenu addSubview: *lMenu];
NSLog(#"\nPointer Inside: %#\n", *lMenu); // <--------- INSIDE WORKS
}
- (void) menuBuild{
pSelf = self;
theString = #"";
vMenu = [[UIView alloc] initWithFrame:CGRectMake(0,0,320,25)];
[pSelf.view addSubview:vMenu];
[vMenu setBackgroundColor:[UIColor grayColor]];
iTime = 2;
[self menuItem:vMenu menuButton:&bMenu_time menuLabel:&lMenu_time menuPosX:240+20 menuLenX:60 menuTagNum:102 menuText:[NSString stringWithFormat: #"Hold: %d", iTime]];
NSLog(#"\nPointer Outside: %#\n", lMenu_time); // <----- OUTSIDE is NULL ??
}
Things to note:
objects passed by reference in the method parameters use **
to pass the address of an object to one of these parameters, use &object when calling the method
to refer to an object that is passed by reference, prefix the variable with *
Have a look at how iOS uses error parameters in methods like NSString initWithContentsOfFile:error: for more examples of how this approach is used.

Related

Passing an object to selector action

Ok simple question but can't find an answer.
I'v got a button to save information in my app.
I have
NSMutableArray *textFields = [[NSMutableArray alloc] initWithCapacity:5];
UITextField *textField = nil;
This is the information i want to save, i'v got 5 textfields in textFields mutablearray.
[save addTarget:self action:#selector(saveInfo)
forControlEvents:UIControlEventTouchUpInside];
and
-(void)saveInfo {
[[[NSUserDefaults standardUserDefaults] setValue: ????? forKey:#"Phone"];
[[NSUserDefaults standardUserDefaults] synchronize];
}
The question is how to access and get information from like textFields[1].text in my saveInfo void ?
Ok To get things a little bit clearer i'v added the whole class. its not very big, and maybe someone could see thats the problem with my implementation .
#implementation Settings
- (id)init: (TableViewController*) TableControll {
NSMutableArray *textFields = [[NSMutableArray alloc] initWithCapacity:5];
UITextField *textField = nil;
for (int i = 0; i < 3; i++) {
textField = [[UITextField alloc] initWithFrame:CGRectMake(0.0f, 0.0f+(i*35), 120.0f, 30.0f)];
textField.backgroundColor = [UIColor whiteColor];
[textField setBorderStyle:(UITextBorderStyleRoundedRect)];
[TableControll.view addSubview:textField];
[textFields addObject:textField];
[textField release]; textField = nil;
}
UITextField *textName = textFields[0];
textName.placeholder = #"Vardas";
UITextField *textNo = textFields[1];
textNo.placeholder = #"Telefonas";
textNo.keyboardType = UIKeyboardTypeNumberPad;
UITextField *textPin = textFields[2];
textPin.keyboardType = UIKeyboardTypeNumberPad;
textPin.placeholder = #"Pin";
UIButton *button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
button.frame = CGRectMake(150, 20, 160, 30);
[button setTitle:#"Advanced settings" forState:UIControlStateNormal];
[TableControll.view addSubview:button];
UIButton *save = [UIButton buttonWithType:UIButtonTypeRoundedRect];
save.frame = CGRectMake(150, 60, 160, 30);
[save setTitle:#"Save settings" forState:UIControlStateNormal];
[TableControll.view addSubview:save];
[button addTarget:self action:#selector(goAdvanced)
forControlEvents:UIControlEventTouchUpInside];
[save addTarget:self action:#selector(saveInfo)
forControlEvents:UIControlEventTouchUpInside];
return self;
}
-(void)goAdvanced {
AppDelegate *newControll = (AppDelegate*)[UIApplication sharedApplication].delegate;
[newControll ChangeController];
}
-(void)saveInfo {
for(int i=0;i<5;i++) {
UITextField *tempTxtField=[_textFields objectAtIndex:i];
NSLog(#"do it %#",tempTxtField.text);
}
}
#end
What you'd want to do if you are using interface builder is create a bunch of IBOutlet for your textfields instead of keeping them in an array. Check out this: tutorial
Now it looks like you're creating things by hand, so in this case, you probably just want to declare your array as #property so it can be accessed by your save method.
To access the text property of a UITextField from your array you would write:
((UITextField *)[textFields objectAtIndex:1]).text
If you want to access the all textfield,
for(int i=0;i<[textFields count];i++) {
UITextField *tempTxtField=[textFields objectAtIndex:i];
NSlog(#"%#",tempTxtField);
}
-(void)saveInfo {
NSMutableArray *tempArr = [NSMutableArray array];
for(int i = 0; i < [textFieldsArray count]; i++){
[tempArr addObject:[(UITextField*)[textFieldsArray objectAtIndex:i]text]];
}
[[[NSUserDefaults standardUserDefaults] setValue:tempArr forKey:#"Phone"];
[[NSUserDefaults standardUserDefaults] synchronize];
}
I'm not entirely sure I understand your question, but this is what I would do in your place. Instead of creating an array to hold each of the textfields, I'd just assign them a custom tag. Any number will work (although avoid zero since that's the default tag on all views). So your init method would look like this:
for (int i = 0; i < 3; i++) {
textField = [[UITextField alloc] initWithFrame:CGRectMake(0.0f, 0.0f+(i*35), 120.0f, 30.0f)];
textField.backgroundColor = [UIColor whiteColor];
textField.tag = (i+1);
[textField setBorderStyle:(UITextBorderStyleRoundedRect)];
[TableControll.view addSubview:textField];
[textField release]; textField = nil;
}
Then in the code that you want to reference the text field, you'd retrieve the view by its tag, making sure to cast it as UITextField.
-(void)saveInfo {
for(int i=0;i<5;i++) {
UITextField *textField = nil;
UIView *view = [self.view viewsWithTag:i];
if ([view isKindOfClass:[UITextField class]]) {
textField = (UITextField *) view;
NSLog(#"do it %#",textField.text)
}
}
}
NOTE: I'm referencing self.view but you'll need to reference TableControll.view. You'll need to retain a reference to it somewhere in your code. Also, I'd recommend your start using ARC.
Hope that helps! Good luck!

UILabel text not changing?

Unable to change text in lMenu_time (this is a UILabel) after it was initially set.
The call back is executed, I tested this, but the text won't change.
?? I am passing around the pointer and making adjustments to the UILabel. ??
lMenu_time and numerous others are defined in the header file. (not seen here)
UILabel *lMenu_time;
...
-(void) NewNumber: (UIButton*) btn {
if (btn.tag == 102){
iTime++;
[lbl setText:#"time"];
if(iTime > 20){iTime=1;}
[lMenu_time setText:[NSString stringWithFormat: #"Hold: %d", iTime]];
}
....
}
- (void) menuItem: (UIView*)vMenu menuButton:(UIButton*)bMenu menuLabel: (UILabel*)lMenu menuPosX: (double)posX menuLenX: (double)lenX menuTagNum: (int)tagNum menuText: (NSString*)txtMenu{
bMenu = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[bMenu setFrame:CGRectMake(posX,0,lenX,25)];
[bMenu setTag: tagNum];
[bMenu addTarget:self action:#selector(NewNumber:) forControlEvents:UIControlEventTouchUpInside];
[vMenu addSubview:bMenu];
lMenu = [[[UILabel alloc] initWithFrame:CGRectMake(posX,0,lenX,25)] retain];
[lMenu setBackgroundColor:[UIColor lightGrayColor]];
[lMenu setText:[NSString stringWithFormat: txtMenu]];
[lMenu setFont:[UIFont systemFontOfSize:14 ]];
[lMenu setTextAlignment:UITextAlignmentCenter];
[vMenu addSubview: lMenu];
}
- (void) menuBuild{
pSelf = self;
theString = #"";
UIView *vMenu = [[UIView alloc] initWithFrame:CGRectMake(0,0,320,25)];
[pSelf.view addSubview:vMenu];
[vMenu setBackgroundColor:[UIColor grayColor]];
iTime = 2;
[self menuItem:vMenu menuButton:bMenu_time menuLabel:lMenu_time menuPosX:240+20 menuLenX:60 menuTagNum:102 menuText:[NSString stringWithFormat: #"Hold: %d", iTime]];
...
}
Just before you try to set the next, try adding
NSLog(#"My label is %#",lMenu_time);
Then, if your console outputs "My label is (nil)" you'll know that the problem is that the pointer to lMenu_time isn't being passed around properly.
Did you bind the label object to the controller in Interface builder? If not I would bet this is an retention issue. You do not post the code that builds the UILabel object, so if that is not done in IB, ensure you call retain or it will certainly be out of scope when you try and modify it.

Accessing NSMutableArray Difficulties

I am trying to build an NSMutableArray each time a button is pressed. I am using NSLog to check the array count after each button pressed and the value is zero. THe code is below.
ProjectViewController.h
NSMutableArray *numberQuery
...
#property (nonatomic, retain) NSMutableArray *numberQuery;
ProjectViewController.m
- (void)makeButtons{
UIButton * pickButton;
int y_plot = 150;
int x_plot = 70;
int z = 0;
for(int y = 1; y < 10; y++)
{
for(int x = 1; x < 5; x++){
z++;
pickButton = [UIButton buttonWithType:UIButtonTypeCustom];
pickButton.frame = CGRectMake(x*x_plot, y_plot, 60, 40);
[pickButton setBackgroundImage:[UIImage imageNamed:#"btnUnselected.png"] forState:UIControlStateNormal];
[pickButton addTarget:self action:#selector(digitClick:) forControlEvents:UIControlEventTouchUpInside];
[pickButton setTitle:[NSString stringWithFormat:#"%d",z] forState:UIControlStateNormal];
pickButton.titleLabel.textColor = [UIColor blackColor];
pickButton.tag = z;
[self.view addSubview:aButton];
}
y_plot=y_plot+45;
}
}
//************************
- (void)digitClick:(id)sender{
UIButton * chosenButton =(UIButton *)sender;
if ([sender isSelected] ==FALSE) {
[sender setSelected:TRUE];
[chosenButton setBackgroundImage:[UIImage imageNamed:#"btnSelected.png"] forState:UIControlStateNormal];
chosenButton.titleLabel.textColor = [UIColor whiteColor];
if([queryNumbersX count]<6){
[self numberSearchArray:chosenButton.tag];
}
}
else
{[sender setSelected:FALSE];
[chosenButton setBackgroundImage:[UIImage imageNamed:#"btnUnselected.png"]forState:UIControlStateNormal];
chosenButton.titleLabel.textColor = [UIColor blackColor];
}
forState:UIControlStateNormal];
NSLog(#"clicked button with title %d",chosenButton.tag);
}
//************************
-(void) numberSearchArray:(NSInteger)newNumber;
{
[self.numberQuery addObject:[NSNumber numberWithInt: newNumber]];
NSLog(#"numberSearchArray %d - count %d",newNumber, [self.numberQuery count]);
}
Am I using the NSMutableArray in the right manner...declaration?
This is the code in the viewDidLoad method
NSMutableArray *numberQuery = [[NSMutableArray alloc] initWithObjects:nil];
Although I have the array declared int he header file, It seems as though I cannot access it outside of the method in which it was allocated.
#property (nonatomic, retain) NSMutableArray *numberQuery;
You must balance that with
#synthesize numberQuery;
in the .m
and the correct creation would be
self.numberQuery = [NSMuatableArray arrayWithCapacity:someNumber];
By doing this
NSMutableArray *numberQuery = [[NSMutableArray alloc] initWithObjects:nil];
You are not using your #property you are creating a new variable, this is why you get the warning that your variable is not use, because it's scope is actually just the viewDidLoad method instead of the object.
It looks like that you have never alloced numberQuery. So it is always nil and thus addObject method is ignored. You need to alloc this in init (or any suitable place you like) and release in dealloc (or in other suitable place).

How to set up a button to invert two textFields

I'm an absolute beginner, and I need to have a button to invert two text fields:
text1 <—> text2
- (IBAction)swapText {
name.text = surname.text;
surname.text = name.text;
}
I know that I have to retain the values and then release them, but I'm not sure how to write it.
This is quite simple: the only text / NSString you have to retain is the one that will stop being hold by the UITextField itself, namely the name.text that you will replace by surname.text.
- (IBAction)swapText {
NSString* temp = [name.text retain];
name.text = surname.text;
surname.text = temp;
[temp release];
}
The only objects you need to take care of would be alloc/deallocating the UITextFields. Assuming your UITextfields are ivars (declared in your header file) you can use the following code:
- (void)viewDidLoad
{
[super viewDidLoad];
CGRect b = [[self view] bounds];
_txt1 = [[UITextField alloc] initWithFrame:CGRectMake(CGRectGetMidX(b)-100, 50, 200, 29)];
[_txt1 setBorderStyle:UITextBorderStyleRoundedRect];
[[self view] addSubview:_txt1];
_txt2 = [[UITextField alloc] initWithFrame:CGRectMake(CGRectGetMidX(b)-100, 100, 200, 29)];
[_txt2 setBorderStyle:UITextBorderStyleRoundedRect];
[[self view] addSubview:_txt2];
UIButton* btnSwap = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[btnSwap setFrame:CGRectMake(CGRectGetMidX(b)-75, 150, 150, 44)];
[btnSwap setTitle:#"Swap Text" forState:UIControlStateNormal];
[btnSwap addTarget:self action:#selector(tappedSwapButton) forControlEvents:UIControlEventTouchUpInside];
[[self view] addSubview:btnSwap];
}
- (void)tappedSwapButton
{
NSString* text1 = [_txt1 text];
NSString* text2 = [_txt2 text];
[_txt2 setText:text1];
[_txt1 setText:text2];
}
- (void)dealloc
{
[_txt1 release];
[_txt2 release];
[super dealloc];
}
Based on your code given, your text in the first textfield will be lost. The easiest way to fix that is to declare a temp NSString object that will hold the string that is contained in name.text:
- (IBAction)swapText{
// create a temp string to hold the contents of name.text
NSString *tempString = [NSString stringWithString: name.text];
name.text = surname.text;
surname.text = tempString;
}
Since you are using dot notation, it is assumed that "name" and "surname" are properties for the IBOutlet references to both textfields that you want to swap. If this is the case, as long as you have "retain" for both of these properties, that will take care of the memory management (as long as you release these in the dealloc method in the .m file).

Why am I getting "Message sent to deallocated instance" in Objective-C?

I have several buttons on my app that are being created dynamically. They are all pointed at the button click event when pressed. When the button pressed method is called, the sender's tag (int value) is parsed into the controller's house ID. It works with one of the buttons — the first one created, to be specific — but the others throw the following error:
-[CFNumber intValue]: message sent to deallocated instance 0xc4bb0ff0
I am not releasing these buttons anywhere in my code. I haven't set them to autorelease or anything like that. I'm just wondering why they are doing this on the click.
The button click event:
- (IBAction) ButtonClick: (id) sender
{
HouseholdViewController *controller = [[HouseholdViewController alloc] initWithNibName:#"HouseholdViewController" bundle:nil];
controller.delegate = self;
controller.HouseID = [(NSInteger)[(UIButton *)sender tag] intValue]; //this line throws an error
controller.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
[self presentModalViewController:controller animated:YES];
[controller release];
}
Where I am creating the buttons:
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
button.frame = CGRectMake(MyLongInScreenCoords, MyLatInScreenCoords, 50, 50);
UIImage *buttonImageNormal = [UIImage imageNamed:#"blue_pin.png"];
UIImage *strechableButtonImageNormal = [buttonImageNormal stretchableImageWithLeftCapWidth:50 topCapHeight:50];
[button setBackgroundImage:strechableButtonImageNormal forState:UIControlStateNormal];
[self.view addSubview:button];
button.tag = [NSNumber numberWithInt:[[words objectAtIndex: i] intValue]];
ButtonPoints[CurrentHouseCount][0] = button;
ButtonPoints[CurrentHouseCount][1] = [NSValue valueWithCGPoint:CGPointMake(MyActualLat, MyActualLong)];
[button addTarget:self action:#selector(ButtonClick:) forControlEvents:UIControlEventTouchUpInside];
CurrentHouseCount++;
tag is an integer property, not an object. just set it
button.tag = [[words objectAtIndex: i] intValue];
and use:
controller.HouseID = [(UIButton *)sender tag];