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];
Related
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...
I'm having issues with data persisting inside of multiple instances of objects I'm creating.
I have a class "IconViewController" that extends UIViewController that I pass information to, such as the name of the image it should be using:
//IconViewController.h
#interface AppIconViewController : UIViewController
{
NSString *imageName;
}
#property (nonatomic, retain) NSString *imageName;
- (void) doSomething;
//IconViewController.m
#implementation AppIconViewController
#synthesize imageName;
NSNumber *iconWidth;
- (void)loadView
{
[super loadView];
UIImageView *iconImage = [[UIImageView alloc] initWithImage:[UIImage imageNamed:imageName]];
iconWidth = [NSNumber numberWithFloat:iconImage.bounds.size.width];
[iconImage release];
NSLog(#"iconWidth: %f", [iconWidth floatValue]);
}
- (void) doSomething
{
NSLog(#"iconWidth: %f", [iconWidth floatValue]);
}
In another view controller, I'm instantiating several instances of these IconViewControllers and passing different sized images to them:
AppIconViewController *appIcon1 = [[AppIconViewController alloc] initWithNibName:nil bundle:nil];
appIcon1.imageName = #"Image65PXWide.png";
[self.view addSubview:appIcon1.view];
AppIconViewController *appIcon2 = [[AppIconViewController alloc] initWithNibName:nil bundle:nil];
appIcon2.imageName = #"Image105PXWide.png";
[self.view addSubview:appIcon2.view];
Okay, the weirdness is that when I'm creating these, I'm getting logs back that are accurate...appIcon1 logs "iconWidth: 65.0" and appIcon2 logs "iconWidth: 105.0". But when I call:
[appIcon1 doSomething];
...my log is "iconWidth:105.0".
Why is the data in the first instance reflecting the data in the second instance? What am I missing?
EDIT:
I know that if I declare iconWidth in the header and synthesize it as a property, that it will work. So what I'm wondering is how to make a private version of it persist. Because I tried retaining the NSNumber with:
iconWidth = [[NSNumber numberWithFloat:iconImage.bounds.size.width] retain];
...and it still doesn't work. Does it have to be synthesized and public?
EDIT #2:
Okay, so I figured out that once I declare iconWidth in my header, it works just fine, and I don't have to synthesize it so it keeps it private. Not sure why exactly it won't work if declared in the implementation file - does anyone have any insight into why and if there's any purpose in declaring variables at the top of an implementation but not in the header? Just curious now more than anything.
Synthesizing doesn't make anything private. It just generates getter/setter methods according to declared properties.
By placing iconWidth in the implementation, outside of any methods, it's essentially a class-level variable. So it gets overwritten by the last thing that writes to it. You already have imageName declared in the interface, so why not just put iconWidth there as well (instance variables are private by default)?
In your method doSomething, you are assuming iconWidth is set up properly.
However, iconWidth is created as an autorelease object in loadView method, so when loadView finishes, the main loop will release iconWidth and you are getting random values.
To fix this you have to retain iconWidth so you can use it in other methods
iconWidth = [[NSNumber numberWithFloat:iconImage.bounds.size.width] retain];
As a general rule, method that doesn't start with init will return autorelease object, so you have to be careful how you instantiate an object and whether you need to call retain on it.
Sorry about the title being extremely vague, I'm new to Objective C and struggling a little with it. Basically I have the following section of code:
Graph *graph1 = [[Graph alloc] init];
[graph1 addNode:#"TEST"];
which is working to a degree. But I want to change it because the above code happens on a button press, and therefore I assume I am creating a new "*graph1" every time I do this. I thought I could simply change it to this:
if(self = [super init])
{
[self setGraph: [[Graph alloc] init]];
}
return self;
Where the above is in the init method, and below is the modified function:
[graph addNode:#"TEST"];
However when debugging I've found addNode method is never called when it's like this.
Thanks
Zac
This is testViewController.h
#import <UIKit/UIKit.h>
#class Graph;
#class Node;
#interface testViewController : UIViewController {
Graph *graph;
UILabel *label;
}
#property (nonatomic, retain) IBOutlet UILabel *label;
#property (nonatomic, retain) Graph *graph;
- (IBAction) buttonPressed:(id)sender;
#end
This is textViewController.m
#import "testViewController.h"
#import "Graph.h"
#implementation testViewController
#synthesize label, graph;
- (id)init
{
if(self = [super init])
{
[self setGraph: [[Graph alloc] init]];
}
return self;
}
- (IBAction)buttonPressed:(id)sender
{
//Graph *graph1 = [[Graph alloc] init];
[graph addNode:#"TEST"];
Node *node1 = [[Node alloc] initWithLabel: #"LABEL"];
label.text = node1.label;
}
The first thing that comes to mind is that graph is nil and thus invoking a method (sending a message) to it will result in nothing. An unwanted release will cause an EXC_BAD_ACCESS, and this seems to be not the case.
I suppose you are calling all of this in a UIViewController subclass, right? Are you sure the right init is called? If you are using a NIB you should override the -(id)initWithNibName:bundle: and place you code there. I guess the code is probably in the plain -(id)init, since you are calling [super init] and not [super initWithNibName:nameOrNil bundle:bundleOrNil], but this way, if initialize the controller with the NIB you custom code is never called and thus graph is nil.
By the way, if the graph property is (retain) you are also causing a memory leak in the init.
I'm sure you're through this problem now, but I agree that the reason "add" is "not being called" is that your graph object is nil at that moment
I'd say, first put a test message around your "addNode" call
NSLog(#"We tried to add here");
[graph addNode:#"TEST"];
That will show you that the add is being called -- I bet it is.
Then, where you had your previous call to initialize "graph" right before your add call, try conditionally initializing it:
if(graph == nil) graph = [[Graph alloc] init];
[graph addNode:#"TEST"];
Note, this is all just to find the problem. Finally I'd say you have some challenges in here with how you are dealing with memory. You may not have reference issues, but later leaks. And depending upon how often this code is executed it could be an issue.
But at least you may get to your issue easier with the above tests.
Have you declared the graph variable in the header? ie: Graph *graph; and the corresponding #property (nonatomic, retain) Graph *graph;
Then I would do this:
-(id) init {
graph = [[Graph alloc] init];
[graph retain];
}
that might help (the only reason I think it wouldn't would be because a) if your variable wasn't declared then you would get a warning like graph may not respond to addNode and if it wasn't retained then your app would crash when it runs)... other than that, I can't see what would be the problem. If that doesn't work, can you please post all your code from your .h and .m files?
Then I would do this:
-(id) init {
graph = [[Graph alloc] init];
[graph retain];
}
that might help
This would result in a memory leak. The retain count of the object pointed to by graph will have a retain count of 2. Not ideal. If you declare the property with the retain attribute then
[self setGraph:[[[Graph alloc] init] autorelease]];
should do it. I go with -
self.graph = [[[Graph alloc] init] autorelease];
There could be many reasons the addNode: method is not being called. Put break points in the enclosing method and see if everything is working as you expect it to.
I hope someone can tell me why I'm wrong. Here is a snippet of my code:
TimeLogAppDelegate *appDelegate = (TimeLogAppDelegate *)[[UIApplication sharedApplication] delegate];
PickFromListViewController * pl = [[PickFromListViewController alloc] initWithNibName:#"PickList" bundle:nil];
pickList = pl;
[pickList setSearchItems:[appDelegate tableListMutableArray:type ] :NSLocalizedString(type,nil)];
pickList.callingViewController = self;
[pl release];
pickList.responseSelector = [[type lowercaseString] stringByAppendingString: #"Selected:"];
pickList.includeNone = YES;
pickList.includeNew = YES;
[self.navigationController pushViewController:pickList animated:YES];
As you can see, I am releasing pl half-way through, just to create the problem. 'pickList' is obviously a PickFromListViewController and is declared in the Header. I set it up as a property (#property (nonatomic, retain) PickFromListViewController *pickList;) and I #synthesize it.
My problem is:
after pl is release I get a BAD ACCESS error accessing pickList indicating that the pointer is no longer available, but I thought the fact that pickList is synthesized, it would be retained until I release it at dealloc?
Can someone tell me why I am wrong?
Many thanks
Setting up your pickList property as (nonatomic, retain) only applies if you access the property via a property accessor, as follows:
self.pickList = P1;
or
[self setPickList:P1];
If you simply assign a value to the member variable:
pickList = P1;
You are bypassing the property accessor methods, so no retain message is sent.
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