iPhone - How to change String in Label properly? - iphone

I am currently learning to program on iPhone.
I currently have a small test app with a mock up keypad, so I have a few buttons that when pressed should place some text in a label.
I then take whats in the label and use it to open an ABUNKnownPersonControllerView for use there. Just a basic app to get used to a few things in the iPhone.
Where I am having trouble seems to be with working with the string used to display what I want in the label. I get the EXC_BAD_ACCESS error so I've re-read the memory management docs but I cant find anywhere that there should be a memory leak.
So I think my problem is I am incorrectly working with the String. My code is as follows:
//Header file
#interface KeyPadViewController : UIViewController <ABUnknownPersonViewControllerDelegate>{
IBOutlet UILabel *phoneNumber;
//NSString *number;
}
#property (nonatomic, retain) UILabel *phoneNumber;
//#property (nonatomic, retain) NSString *number;
-(IBAction)addNum1;
-(IBAction)addNum2;
-(IBAction)showUnknownPersonViewController;
//Implementation file
- (void)viewDidLoad
{
[super viewDidLoad];
self.title = NSLocalizedString(#"Keypad",#"Keypad");
//self.number = #"";
self.phoneNumber.text = #"";
}
-(IBAction)addNum1{
//self.number = [self.number stringByAppendingString:#"1"];
self.phoneNumber.text = [self.phoneNumber.text stringByAppendingString:#"1"];
}
-(IBAction)addNum2{
//self.number = [self.number stringByAppendingString:#"2"];
self.phoneNumber.text = [self.phoneNumber.text stringByAppendingString:#"2"];
}
And the showUnknownPerson view in the implementation file:
-(void)showUnknownPersonViewController
{
//NSString *contactNum = self.phoneNumber.text;
ABRecordRef aContact = ABPersonCreate();
CFErrorRef anError = NULL;
ABMultiValueRef email = ABMultiValueCreateMutable(kABPersonPhoneProperty);
bool didAdd = ABMultiValueAddValueAndLabel(email, self.phoneNumber.text, kABPersonPhoneMobileLabel, NULL);
if (didAdd == YES)
{
ABRecordSetValue(aContact, kABPersonPhoneProperty, email, &anError);
if (anError == NULL)
{
ABUnknownPersonViewController *picker = [[ABUnknownPersonViewController alloc] init];
picker.unknownPersonViewDelegate = self;
picker.displayedPerson = aContact;
picker.allowsAddingToAddressBook = YES;
picker.allowsActions = YES;
[self.navigationController pushViewController:picker animated:YES];
[picker release];
}
else
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Error"
message:#"Could not create unknown user"
delegate:nil
cancelButtonTitle:#"Cancel"
otherButtonTitles:nil];
[alert show];
[alert release];
}
}
CFRelease(email);
CFRelease(aContact);
}
And the dealloc:
- (void)dealloc
{
[phoneNumber release]; //#synthesize
// [number release]; //#synthesize
[super dealloc];
}
I have tried using a Mutable string but have the same error, I've also tried not releasing the UILabel and the NSString and have the same issue.
When my app loads if I press one button and one character appears in the label, if I then use the showUnknowPersonViewController everything works fine, have if I press button 1 and then button 2 so that I know have two characters in the UILabel ("12") and select to use the showUnknownPersonViewcontroller then I get the crash.
*** -[CFString release]: message sent to deallocated instance 0x1226a0
However I don't understand where it could be getting deallocated?
This is probably something basic I've missed in the tutorials but I cant figure it out, can anyone see what I might be doing wrong?
EDIT: Just to add if the self.phoneNumber.text (UILabel) returns either empty or one character it works fine however if its 2 or more I get the crash.
EDIT 2: I have removed my NSString from the code completely and I still have the same issue, so it looks like its the UILabel thats causing the issue? See code changed above.

I think the problem may be in the line:
self.phoneNumber.text = number;
You are setting self.phoneNumber.text to the same string as number, not to a copy or to a newly created string with the same content. Thus, in your addNum methods, the setter of one property releases the string when it resets its value. The second property is still pointing to the released string. When the setter of the second property is called, it tries to release that string again. Hence, the error.
If this is the problem, it could be solved by rewriting the line as:
self.phoneNumber.text = #"";
Although, even this is probably not necessary. All instance variables are initiated to nil, and it would not be necessary to give it an initial value.

I guess your NSString variable number is causing the problem. It seems the values you assign to that variable are not retained anywhere. As you are using self to access the variable phoneNumber, you need use that for accessing the number too. Using self calls the setter for the particular property where the property is actually retained.

I believe the property might also need to be declared as an IBOutlet, and, if so, that could be causing the problem.
#property (nonatomic, retain) IBOutlet UILabel *phoneNumber;

It seems you never allocate number. What is the initial value of number?
To my mind, you should allocate a string in your viewDidLoad

Related

Passing Value of Text Field Between Views iphone

thanks for helping a novice. I'm trying to use the value of a Text Field in SecondViewController.xib to set up a settings string in WebService.m. I've included my code. When I execute the application, my NSLog from WebService give me an output of "The test IP is: (null)" The NSLog from SecondViewController.m is the value of the Text Field. How do you pass the strIP to WebService.m correctly? Using the example code would be very helpful.
SecondViewController.h:
#interface SecondViewController : UIViewController
{
UITextField *ipAdd;
NSString *strIP;
}
#property (nontoxic, retain) IBOutlet UITextField *ipAdd;
#property (nonatomic, retain) NSString *strIP;
-(IBAction)textchanged:(id)sender;
+(SecondViewController*)sharedIP;
SecondViewController.m:
-(IBAction)textchanged:(id)sender
{
strIP = ipAdd.text;
NSLog(#"the string in the text field is: %#", strIP);
}
+(SecondViewController*)sharedIP
{
static SecondViewController *sharedIP = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedIP = [[SecondViewController alloc] init];
});
return sharedIP;
}
WebService.m:
SecondViewController *IP = [SecondViewController sharedIP] ;
NSLog(#"The test IP is: %#", IP.strIP);
If you want to modify a value in a block, you need to use the __block type specifier on the variable type (sharedIP) so modifications in the block affect the value outside the block.
Also: every time you call +sharedIP, you set the value of the static to nil and only reset it the first time. Your design seems a bit odd... I'm not sure why you would ever want a static instance of a view controller, but I don't know what you're after. If you just want persistent data, see NSUserDefaults (which is cool because it persists over restarts). It might be better to create a static instance of a data container to persist your string value.
Good luck.
Check delegate of your textField that should be your SecondViewController.
Try the print the same value before you return. e.g. :
+(SecondViewController*)sharedIP
{
static SecondViewController *sharedIP = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedIP = [[SecondViewController alloc] init];
});
**NSLog(#"The test IP is: %#", sharedIP.strIP);**
return sharedIP;
}
Might help
Cheers

NSArray is getting an EXC_BAD_ACCESS

I'm not entirely sure if I wrote this array correct in the first place. Here is the .h in my app delegate.
NSString *text0;
...
NSString *text123;
NSMutableArray *fortunesArray;
}
#property(nonatomic,retain) NSMutableArray *fortunesArray;
#property (nonatomic, retain) IBOutlet UIWindow *window;
#property (nonatomic, retain) IBOutlet AppViewController *viewController;
#end
Then in the app delegate.m I'm assigning all of them like such.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Override point for customization after application launch.
text0 = #"Text here";
...
text123 = #"Text here";
self.fortunesArray = [NSMutableArray arrayWithObjects:text0,text1,text2,text3,text4,text5,text6,text7,text8,text9,text10,text11,text12,text13,text14,text15,text16,text17,text18,text19,text20,text21,text22,text23,text24,text25,text26,text27,text28,text29,text30,text31,text32,text33,text34,text35,text36,text37,text38,text39,text40,text41,text42,text43,text44,text45,text46,text47,text48,text49,text50,text51,text52,text53,text54,text55,text56,text57,text58,text59,text60,text61,text62,text63,text64,text65,text66,text67,text68,text69,text70,text71,text72,text73,text74,text75,text76,text77,text78,text79,text80,text81,text82,text83,text84,text85,text86,text87,text88,text89,text90,text91,text92,text93,text94,text95,text96,text97,text98,text99,text100,text101,text102,text103,text104,text105,text106,text107,text108,text109,text110,text111,text112,text113,text114,text115,text116,text117,text118,text119,text120,text121,text122,text123,nil];
self.window.rootViewController = self.viewController;
[self.window makeKeyAndVisible];
return YES;
}
I have tried this with NSArray and Mutable. The EXC_BAD_ACCESS is showing up pointing at text3 and before it was pointing at text5. If I cut out everything after about 50 the screen will open but when I finally try to have it work by clicking the button it resorts back to that bad access. (So can't tell if there is an issue with the views button yet because this issue is happening at this array repeatedly.) I'll post the code that calls it, but I'm pretty sure the main issue has something to do with this array.
In my view controller.m
-(IBAction)ganjaButton:(id)sender{
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
int pressCount;
NSString *display;
if(pressCount%2==0){
[sender setBackgroundImage:[UIImage imageNamed:#"nug2.png"] forState:UIControlStateNormal];
display = [[NSString alloc] initWithFormat:#"%#",[appDelegate.fortunesArray objectAtIndex:40]];
}
else{
[sender setBackgroundImage:[UIImage imageNamed:#"nug1.png"] forState:UIControlStateNormal];
display = [[NSString alloc] initWithFormat:#"%#",[appDelegate.fortunesArray objectAtIndex:44]];
}
pressCount++;
label.text = display;
[display release];
}
Also yes in the above code the part that says AppDelegate is actually my AppDelagtes name.
Any help would be appreciated. Thanks.
Have you thought about storing all those text values in a plist and loading them into an array with [NSArray arrayWithContentsOfFile:filePath];?
That would be a MUCH cleaner solution in the first place.
Edit: To get the filePath, assuming your plist is named "textStrings.plist" you would use the following:
NSString *filePath = [bundle pathForResource:#"textStrings" ofType:#"plist"];
Try this:
self.fortunesArray = [NSMutableArray arrayWithObjects:text0, text1, text2, nil];
I don't know if there's a maximum number of parameters that you can pass into a method, but if there is it's likely that you're exceeding that limit at 124, and probably also at 50. If everything works fine when you pass just a few objects into the array, you should just find a different way to create the array. Another answer mentions using a property list, which would be a fine solution. You could also use a plain old text file with some delimiter between strings, read that into a single string, and use NSString's -componentsSeparatedByString: method to create an array.
On the other hand, if you still have trouble with just a few objects in the array, you'll know that the problem lies elsewhere. I don't see any obvious problems, but I'd be on the lookout for other places in your code where the fortunesArray property is set.
I think the array is getting autoreleased and thats why the crash is appearing. Try allocating memory for the array
self.fortunesArray =[[NSMutableArray alloc] initWithObjects:text0,text1...text123,nil];
and release the array once you are done with it. But I will Strongly recommend using plist file as suggested by Christopher.

message sent to deallocated instance ! cant find error

alright I am looking for this error since 2 hours and I just cant figure it out please help me.
I have the following situation I have 2 viewcontroller.
one presents the other one as modalview like that.
SearchViewController *searchViewController = [[SearchViewController alloc]init];
[searchViewController setModalTransitionStyle:UIModalTransitionStyleCoverVertical];
searchViewController.delegate = self;
searchViewController.senderTag = [sender tag];
[self presentModalViewController:searchViewController animated:YES];
[searchViewController release];
in my searchviewcontroller I do this in the .h file
BSKmlResult *selectedAirport;
#property (nonatomic, retain) BSKmlResult *selectedAirport;
in the .m file i synthesize it and then set it like that
selectedAirport = [self.airportList objectAtIndex:indexPath.row];
and then release it here
- (void)dealloc {
[selectedAirport release];
[super dealloc];
}
in the delegate methode of my SearchViewController which is implemented in the first
viewcontroller where I also present the SearchViewController
i have the following
if (controller.selectedAirport) {
if (departureAirport) {
[departureAirport release];
}
departureAirport = [controller.selectedAirport copy];
}
[self dismissModalViewControllerAnimated:YES];
I narrowed down where the error happens it is in the dealloc of my SearchViewController
[selectedAirport release];
but I cant figure out where my mistake is
please help
selectedAirport = [self.airportList objectAtIndex:indexPath.row];
You arent retaining selectedAirport here.
Change it to
self.selectedAirport = [self.airportList objectAtIndex:indexPath.row];
Since you couldnt find it out, probably you dont know this...
If you dont access member variables by self.memberVariable, you are not accessing its property. Thus, it was not getting retained.
Ofcourse you can also retain it by saying
selectedAirport = [[self.airportList objectAtIndex:indexPath.row] retain];
But whats the use of your property then...
You need to use self. to run it through the synthesized method, to get the retain.
self.selectedAirport = [self.airportList objectAtIndex:indexPath.row];
I know this post is quite old but just wanted to add something useful to it. In the above case the member variable name and property name are identical so you may still by mistake set the value of member variable instead accessing it using property that will call retain implicitly. Hence the best way to make sure you always use self.selectedAirport is to name the member variable something different than your property.
For example, in .h file you can have below implementation:
NSString *_selectedAirport;
then encapsulate it inside a property like below
#property(nonatomic,retain) NSString *selectedAirport;
and in .m implementation file synthesize it like below:
#synthesize selectedAirport = _selectedAirport;
By doing above, if you try to access it like below
selectedAirport = [self.airportList objectAtIndex:indexPath.row];
then it would result in an error and you will be prompted to use self.selectedAirport.
Also in this case your dealloc method can have either
self.selectedAirport = nil;
or
[_selectedAirport release];

Method call in Objective-C

I'm new to Objective-C and iPhone SDK development. I want to call a method in the same class:
- (void) setFilePath:(NSString *) p
{
[self methodCall];
}
- (void) methodCall
{
fileContent.text = #"Test"; //fileContent is a UITextView
}
If the property "filePath" is set, the method "setFilePath" is called. Then the UITextView, created in IB, should display the text. But that doesn't work ...
If I call the method directly via button in IB, then the UITextView changes his content successfully:
- (IBAction) clickButton
{
fileContent.text = #"Test";
}
What could be the problem?
Thanks for your answers!
EDIT 2: I solved the problem by setting "filePath" after pushing the view:
- (IBAction) showFileContent {
FileContentsViewController *fileContentsViewController = [[FileContentsViewController alloc] init];
[self.navigationController pushViewController:fileContentsViewController animated:YES];
fileContentsViewController.filePath = self.filePath;
fileContentsViewController.title = [NSString stringWithFormat:#"Content from von %#", [filePath lastPathComponent]];
[fileContentsViewController release];
}
EDIT 1: Here's the code of my interface:
#interface FileContentsViewController : UIViewController {
NSString *filePath;
UITextView *fileContent;
}
- (void) methodCall;
#property (nonatomic, retain) NSString *filePath;
#property (nonatomic, retain) IBOutlet UITextView *fileContent;
#end
... and here's the code of the implementation:
#import "FileContentsViewController.h"
#implementation FileContentsViewController
#synthesize filePath;
#synthesize fileContent;
- (void) setFilePath:(NSString *) p
{
NSLog(#"setFilePath executed!");
[self methodCall];
}
- (void) methodCall
{
fileContent.text = #"Test"; // UITextView
}
// some standard methods
#end
... and finally the code of the method that sets "filePath":
- (IBAction) showFileContent {
FileContentsViewController *fileContentsViewController = [[FileContentsViewController alloc] init];
fileContentsViewController.filePath = self.filePath;
fileContentsViewController.title = [NSString stringWithFormat:#"Content from von %#", [filePath lastPathComponent]];
[self.navigationController pushViewController:fileContentsViewController animated:YES];
[fileContentsViewController release];
}
What it looks like is that the fileContentsViewController created in -showFileContent doesn't have anything assigned to its FileContentsViewController.fileContent (or, at least, fileContent doesn't point to a UITextView that gets displayed) when fileContentsViewController.filePath is set.
You set filePath immediately after creating fileContentsViewController. If FileContentsViewController's -init doesn't create an appropriate fileContent, then when -setFilePath: is called from -showFileContent, there's no fileContent to set the text of. If fileContentsViewController is a typical view controller, fileContent won't exist until fileContentsViewController is loaded, which (I believe) happens during -pushViewController:animated.
One fix is to override -setFileContent to set fileContent.text as appropriate:
-(void)setFileContent:(UITextView*)fileContentView {
if (fileContent != fileContentView) {
[fileContent release];
fileContent = [fileContentView retain];
if (self.filePath) { // if file path is not nil
fileContent.text = ...;
}
}
}
Another other fix is to ensure you only set filePath when fileContent exists, but this is more brittle. A third is to set filePath after you push fileContentsViewController.
The way you would discover the cause during debugging is to check two things: execution ("Is the code I'm expecting to be executed ever reached?") and data ("Do the variables hold the values I expect?"). Set breakpoints in -showFileContent and -methodCall so you know that the methods are being called (which would be one reason for failure). If execution makes it into -methodCall, the problem must be something else. From there, examine the values of the variables used in -methodCall and you'll discover fileContent is either nil or not the same fileContent that shows up later.
Have you checked that fileContent has been set up at the time setFilePath is called? If you're trying to set things up at start up then it's possible that you're making calls before the views have been loaded (which the OS delays until the last possible moment).
You can force views to load by calling [self view] just before you try to access any of your Interface Builder views (NB don't call loadView - that doesn't do what you'd think).
If the problem is that setFilePath: is not called that I would guess that your code looks like
filePath = #"some value";
when it should be
self.filePath = #"some value";
When using #property you need to use self.filePath to call the methods, otherwise you will just access the ivar directly.
How have you define filePath property ?
I think that it is the problem...

Why is this line of Objective-C leaking memory?

I'm writing an iPhone app. I have a header file that looks like this:
#interface EditTagsViewController : UITableViewController {
NSMutableArray *allTags;
NSMutableArray *selectedTags;
NSInteger currentFavorite;
}
#property (nonatomic, retain) NSMutableArray *allTags;
#property (nonatomic, retain) NSMutableArray *selectedTags;
#property (nonatomic) NSInteger currentFavorite;
#end
In the implementation file, my viewDidLoad method looks like this:
- (void)viewDidLoad {
NSMutableArray *aTags = [[NSMutableArray alloc] initWithArray:[Tag findAllTags]];
self.allTags = aTags;
[aTags release];
NSMutableArray *sTags = [[NSMutableArray alloc] initWithArray:[Tag findByFavoriteId:currentFavorite]];
self.selectedTags = sTags;
[sTags release];
UIBarButtonItem *add = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:#selector(addNewTag:)];
self.navigationItem.rightBarButtonItem = add;
[add release];
[super viewDidLoad];
}
Here is my dealloc method:
- (void)dealloc {
[allTags release];
[selectedTags release];
[super dealloc];
}
What's confusing to me is that when I run the app both in the simulator and on the device itself, using Instruments (memory leaks), it tells me that this line in my viewDidLoad method is leaking an array:
self.selectedTags = sTags;
It's confusing because I'm using the exact same technique with 2 different variables, and yet no leak is reported with the first one.
I feel like I'm missing something obvious here. Any ideas?
Your code looks correct to me. Is it possible that one of [Tag findAllTags] or [Tag findByFavoriteId:] is leaking? Are you making sure to set self.allTags and self.selectedTags to nil in dealloc?
Be mindful of the difference between saying self.allTags = ... and allTags = .... Because allTags is a property and has the retain attribute, whenever you assign via self.allTags = ..., it implicitly calls the setter method [self setAllTags:...], which invokes retain on the new value and release on the old value (if any). You're doing it correctly in this code sample, but if elsewhere you're assigning straight to allTags (without the self.), you're not releaseing the old value, which may be the source of the leak. Likewise for selectedTags.
Have a look at findByFavoriteId is there a retain there? That is the only difference I can see between the aTags and sTags are used in your example