UIVIew subclass object becomes mysteriously nil in tabbar app
Hola caballeros, I am stumped, mystified, and worse yet, stymied: in my first tab bar project I have an object that is instance of a subclass "BarGraph of UIView, which I've used before without trouble. In this project I do an alloc-init in the viewDidLoad method of the second viewController, graphViewController. This sets up the BarGraph. The data which is read in is displayed properly by that object.
The trouble comes when I use a tab to go to a different viewControler where I add to the data. I see the change in the appropriate mutable array. AOK. But when I come back to the other tab, the data displayed by BarGraph is the same as before. After some checking, I found that the BarGraph object had become NULL:
NSLog(@"barGraph:%@", barGraph)
gives "null". All my attempts to fix fail. I see only one alloc-init for this object and no deallocs. These I tracked with NSLog's. Too weird. I await your advice.
Edited to add
Here is my interface:
@interface GraphViewController : UIViewController {
BarGraph *barGraph;
int foo;
}
@property (nonatomic, retain) BarGraph *barGraph;
@property (assign) int foo;
- (void) updateDisplay;
@end
* from epsilon2.7 aka Jim Carlson: * Below is where I instantiate BarGraph in GraphViewController.m. I added one line "foo = 100;" to see if the problem has to do with all ivars or just barGraph. Alas, both foo and barGraph "forget" their values when tabbing away and then tabbing back. I very much appreciate the feedback (and the kindness to a newb).
@implementation GraphViewController
@synthesize barGraph, foo;
- (XTabAppDelegate *) theDelegate {
return (XTabAppDelegate *) [UIApplication sharedApplication].delegate;
}
....
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(@"BARGRAPH: alloc-init");
barGraph = [[BarGraph alloc] initWithFrame:CGRectMake(15, 118, 289, 110)];
foo = 100;
[self.view addSubview:barGraph];
[self updateDisplay];
}
....
@end
Interface for app delegate, where the data graphed is stored:
@interface XTabAppDelegate : NSObject <UIApplicationDelegate, UITabBarControllerDelegate> {
UIWindow *window;
UITabBarController *tabBarController;
LogViewController *logViewController; // good
GraphViewController *graphViewController; // bad
Log *currentLog; // data stored here
}
...
@end
The data in currentLog
persists (NSLog
confirms this in GraphViewController), and I push that data into barGraph with the following code in GraphViewController:
-(void) updateBarGraphForDay {
Log *theLog = [self theDelegate].currentLog;
barGraph.data = theLog.log; // main data stored in the theLog.log
foo += 1;
}
Following up ughoavgfhw's comment, I checked dealloc for both graphViewController and BarGraph, e.g. by inserting an NSLog statement:
- (void)dealloc {
NSLog(@"deallocating GraphViewController");
[barGraph dealloc];
[super dealloc];
}
Neither is called!
I never see the dealloc.
(2) Below is the code for didSelectViewController
. With
xtabBarController.delegate = self;
in place as the first line of the body of the delegate's didFinishLaunchingWithOptions
method, the method didSelectViewController
(code below) is executed. If [graphViewController updateDisplay]
is not commented out, then (a) I detect a nil barGraph object, (b) I do not see the new data displayed (probably since the rele开发者_如何学Cvant message went to a nil object), (c) if I execute [graphViewController updateDisplay]
manually in graphViewController
by pushing a button, then the updated info is displayed.
// Optional UITabBarControllerDelegate method.
- (void)tabBarController:(UITabBarController *)tabBarController didSelectViewController:(UIViewController *)viewController {
NSLog(@"UITabBarControllerDelegate method called");
if (viewController == [tabBarController.viewControllers objectAtIndex:1]) {
NSLog(@"graphViewController selected, graphViewControler = %@", graphViewController);
[graphViewController updateDisplay];
}
... + similar if statements
}
It may be that [graphViewController updateDisplay]
is called too early, when graphViewController
is not initialized (???). I've tried to circumvent these difficulties by implementing viewWillAppear
in graphViewController
. However, NSLog
informs me that viewWillAppear
is never called. Here is the code for viewWillAppear
:
- (void) viewWillAppear:(BOOL)animated {
[super viewWillAppear:TRUE];
NSLog(@"bb:graphViewController, viewWillAppear called");
[self updateDisplay];
}
Not a lot to go on, so shot in the dark here. But, if an object gets released, pointers to that object are not nulled out. They will only be nulled out if they are explicitly set so. Further, the fact that you are seeing the old graph indicates to me that the object exists in memory some where.
So, I would guess that you are storing this pointer in some class, for example A. You have an instance of the class A pointed to by a variable x. Your barGraph is stored in x. At some point in the process of moving from one tab to another. The variable x is reinitialized with a new instance of A. This new instance hasn't had it's barGraph initialized yet, so is null. However, the old instance of A still exists somewhere and still has its pointer to barGraph intact.
Another nearly-complete stab in the dark: your BarGraph object is a synthesized property but within that viewController you're accidentally referring to the iVar (barGraph =
) instead of the property (self.barGraph =
) so it looks right in that viewController but you're logging the property, which is still nil.
Your problem is that the GraphViewController is being released and reloaded when you change tabs. When a view controller is not being used, it is released. Then, when it is needed again, a new one is created. Any persistent information should be stored in a model object which is accessed by the controller object. This is basic model-view-controller programming. When your GraphViewController is created, it should connect to a model object and load its information from that, instead of trying to store it itself.
精彩评论