Method call in Objective-C - iphone

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...

Related

Discrepancy in passing data between classes

Part 1 of the code works fine. Part 2 shows minor changes in the code which causes the code to stop working (without errors/warnings) as expected.
Part 1: (Works)
#import "ClassA.h"
#import "ClassB.h"
#implementation ClassA
- (void) sendData
{
NSString *temp = [NSString stringWithFormat:#"HI!"];
ClassB *classBObject = [[ClassB alloc] init];
classBObject.dataToDisplay = temp;
self.view = classBObject.view;
}
#end
Interface of ClassB:
#import <UIKit/UIKit.h>
#interface ClassB : UIViewController
#property (weak, nonatomic) IBOutlet UILabel *textLabel;
#property NSString * dataToDisplay;
#end
Implementation of ClassB:
#import "ClassB.h"
#implementation ClassB
#synthesize dataToDisplay, textLabel;
- (void)viewDidLoad
{
[super viewDidLoad];
textLabel.text = dataToDisplay;
}
#end
Part 2:
But if I change - (void)sendData of ClassA to the following:
- (void) sendData
{
NSString *temp = [NSString stringWithFormat:#"HI!"];
ClassB *classBObject = [[ClassB alloc] init];
classBObject.textLabel.text = temp; // statement changed from Part 1.
self.view = classBObject.view;
}
and remove textLabel.text = dataToDisplay; from implementation of ClassB, the textLabel on view controller of ClassB does not get updated. Can you please suggest, why is it so?
Thanks!
Edit 1:
In the statement: classBObject.textLabel.text = temp; // statement changed from Part 1., I had missed .text while copy pasting. Please excuse me for that.
The reason that the second technique is incorrect (besides the missing .text at the end of textLabel) is that when you return from the class B initializer, the underlying UILabel corresponding to textLabel undoubtedly has not been created yet. If you look at this diagram you'll see that the view configuration is not completed at the end of the the initialization methods, but rather upon access. So you must defer any access of the user interface controls until viewDidLoad.
Update:
When I run the following code, I get "0x0" in my log, proving that the UILabel on my second view is still nil and has not been initialized yet, as I would have expected. When the viewDidLoad in my second controller sets self.textLabel.text = self.dataToDisplay in viewDidLoad, it works like a champ (as it does for you). But the UILabel IBOutlet property is just not reliable until viewDidLoad.
SecondViewController *controller = [[SecondViewController alloc] init];
NSLog(#"%p", controller.textLabel);
controller.dataToDisplay = #"from first view";
[self presentViewController:controller animated:YES completion:nil];
You are setting a UITextLabel to NSString. Try
classBObject.textLabel.text = temp;
Change this line...
classBObject.textLabel = temp; // statement changed from Part 1.
to
classBObject.textLabel.text = temp; // statement changed from Part 1.
Also, you should do
[self.view addSubView:classBObject.view]; //using navigation controller or presenting modal viewcontroller would be recommended.
instead of
self.view = classBObject.view;
after this line, update the label's text with your value.
classBObject.textLabel.text = temp; // statement changed from Part 1.
I noticed that you're using a weak reference to the UILabel in your classB interface. Any reason you're not using a strong reference? The only time you want to use weak references is to avoid retain cycles. Most likely, your UILabel isn't being retained.
Where do you initialize your UILabel?

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

Sharing data/string with a singleton between views

I'm trying to share a string between two views on my iPhone project. It currently works if I use the actual #"something here" for the string, but if I want to use something like label.text, it doesn't even though it is still a string.
I'll show you what I have to make it clearer.
First View: Info_ViewController.h
#import <UIKit/UIKit.h>
#interface Info_ViewController : UIViewController {
IBOutlet UITextField *locationField;
}
#property (nonatomic, retain) NSString *locationString;
+ (id)sharedInfoVC;
#end
First View: Info_ViewController.m
#import "Info_ViewController.h"
static Info_ViewController *sharedInfoVC = nil;
#implementation Info_ViewController
#synthesize locationString;
#pragma mark Singleton Methods
+ (id)sharedInfoVC {
#synchronized(self) {
if (sharedInfoVC == nil)
sharedInfoVC = [[self alloc] init];
}
return sharedInfoVC;
}
- (id)init {
if (self = [super init]) {
locationString = [[NSString alloc] initWithString:locationField.text]; //This is there part I mentioned earlier, when using #"something" instead of locationField.text works.
}
return self;
}
Second View: Confirm_ViewController.m
#import "Confirm_ViewController.h"
#import "Info_ViewController.h"
#implementation Confirm_ViewController
- (IBAction)buttonZ:(id)sender
{
Info_ViewController *infoVCmanager = [Info_ViewController sharedInfoVC];
locationLabel.text = infoVCmanager.locationString;
}
I put it under a button for now, but it will eventually be under viewDidLoad.
If you replace locationField.text with a string (#"blahblahblah") it won't crash and works.
When it crashes I get the error: Program received signal: "SIGABRT"
EDIT: I tried changing
initWithString:locationField.text
to
initWithFormat:#"%#",locationField.text
and now it my label in the second view prints "(NULL)"
Thanks for taking the time to give advice, I really appreciate it.
It is an error to pass nil as the format string to -[NSString initWithString].
So how are you passing nil? You actually have two instances of Info_ViewController. You have the one instance which is the normal part of your app, and then you also have a second instance which is your "singleton" (which really isn't a singleton any more).
So in your "singleton" instance, the UITextField is nil (and will always be nil) and so locationField.text is nil and you are passing that to initWithString:, which is a crash. In fact the "singleton" isn't even fully baked as view controller's go.
If you want a singleton to share data elsewhere in your app, it really should not be a Info_ViewController or any type of view controller. It should be of some other class that you use to manage your data. I would create another class and implement that as a singleton.
Hope that helps you understand what's happening here.
Pre-pend "self." to your location string.
self.locationString = [[NSString alloc] initWithString:locationField.text];
From what I understand of your code, you have got the value for locationString when you from the textfield when you initialize the viewController. At this point of time, your textfield would not be visible. After it becomes visible and you enter something, you don't have the code to store it to locationString.
What you should do is wait for Info_ViewController object to be initialized and displayed. Then on the press of some button or some other event, assign locationLabel.text from the locationString or even directly from locationField.text.
I would provide code, but I have no clue as to how you are structuring this. If you still need help, please provide the details.

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];

iPhone, error: object cannot be set - either readonly property or no setter found

I know there's another question like this on stack overflow, but my problem is from my own class not a UIButton. My method is a lot more complicated. The first commented line works.
#implementation GettingStartedViewController
- (void)actionSheet:(UIActionSheet *)actionSheet
clickedButtonAtIndex:(NSInteger)buttonIndex {
//[cui showHelpClickButtonAtIndex:buttonIndex:self.view:
//theController:FALSE:HelpPageGettingStarted];
// Error from this next line error: object cannot be set - either
//readonly property or no setter found
self.theController = [cui showHelpClickButtonAtIndex:buttonIndex:
self.view:theController:FALSE:HelpPageGettingStarted];
}
- (void)viewDidLoad {
[super viewDidLoad];
cui = [CommonUI alloc]; // Used for several functions
}
And heres the function it calls. I've trying to reuse code and keep things in one function. the function uses addSubView and presentModalViewController.
#implementation CommonUI
-(UIViewController *)showHelpClickButtonAtIndex:(int)buttonIndex:
(UIView *)vw:(UIViewController *)vc:(BOOL)useNav:(HelpPage)page{
if (buttonIndex == CommonUIInfoHelpPagesBtnIdx) {
if (useNav == TRUE) {
// snip
} else {
vc = [[HelpViewController alloc] initWithNibName:#"HelpView"
bundle:nil onPage:page];
[vw addSubview:vc.view];
return [vc autorelease];
}
} else {
// Snip
}
// Snip
}
The message says that there is no property for theController in GettingStartedViewController. Your method call works just fine.
self.theController = someObject is the same as calling the setter method: [self setTheController:someObject]
Properties automatically generate those getters and setters; so if you did not define a property, it is not going to create a setter and this is your problem here.
Add the following to your header file:
#property (nonatomic, retain) UIViewController* theController;
And synthesize it in your implementation file:
#synthesize theController;
Do not forget to release it in the -dealloc method, as you told the setter to retain the object:
-(void) dealloc {
[theController release];
theController = nil;
}
iPhone, error: object cannot be set - either readonly property or no setter found error comes when you have not synthesized it and still accessing its setter or/and getter method.