1

I'm having trouble assigning an instance of a class to the following UITableViewController subclass:

@interface MyTableViewController : UITableViewController </*usual protocols*/>
@property (nonatomic, retain) MyClass *myClass;
@end

I'm currently assigning a non-null instance of MyClass to an instance of MyTableViewController like this:

MyTableViewController *myTableViewController = [[MyTableViewController alloc] init];
MyClass *nonNullInstanceOfMyClass = [[MyClass alloc] init];

myTableViewController.myClass = nonNullInstanceOfMyClass;

[self.navigationController pushViewController:myTableViewController animated:YES];

[nonNullInstanceOfMyClass release];
[myTableViewController release];

The problem is that myClass is null in MyTableViewController's viewDidLoad. Why is this happening?

Edit #1: I check that nonNullInstanceOfMyClass is not null by NSLogging it.

Edit #2: Provided more code. Also viewDidLoad seems to be called before I push the view controller (which could cause the problem... although it seems odd).

Edit #3: Fixed by moving self.tableView.delegate = nil; and self.tableView.dataSource = nil; from init to viewDidLoad.

SundayMonday
  • 19,147
  • 29
  • 100
  • 154
  • so are you saying that you set it to a non-null instance, then viewDidLoad is called, and when you check myClass in that function it is null? – sashang Sep 23 '11 at 00:07
  • 2
    Describe how you know nonNullInstanceOfMyClass is not null. – thomashw Sep 23 '11 at 00:08

3 Answers3

0

Are you doing anything between instantiating the view controller and assigning to the property? viewDidLoad doesn't necessarily get called when you push the view controller onto the navigation stack - it gets called whenever it needs to load the view. So, for example, NSLog(@"%@", myTableViewController.view); would trigger viewDidLoad, and if you did this before assigning to the property, that would explain this behaviour.

Could you post actual code instead of an approximation please? There could be important details you are leaving out.

Jim
  • 72,985
  • 14
  • 101
  • 108
  • Upon further investigation it appears `viewDidLoad` is called before I assign nonNullInstanceOfMyClass. But I'm assigning nonNullInstanceOfMyClass before I push the view controller. – SundayMonday Sep 23 '11 at 00:18
  • @MrMusic: well doesn't that discovery answer the question? – sashang Sep 23 '11 at 00:19
  • Then the problem is either in the instantiation of your view controller or between that point and the property assignment. At some point you are accessing the view controller's `view` property, which kicks off `viewDidLoad`. – Jim Sep 23 '11 at 00:21
  • @sashang it certainly helps. But I don't know why viewDidLoad would be called before the view is pushed. Please see the additional sample code I provided. – SundayMonday Sep 23 '11 at 00:23
  • 1
    @Jim please see the additional code I provided. I'd like to understand why `viewDidLoad` is called before the view controller is pushed. The only "special" code have in my `init` method is `self.tableView.delegate = self;` and `self.tableView.dataSource = self;` – SundayMonday Sep 23 '11 at 00:26
0

viewDidLoad is fired when the View is loaded from xib.

You have a couple options here.

Easiest is the viewDidAppear: method

- (void)viewDidAppear:(BOOL)animated
{
    // Handle what you want for when your view has become visible. 
    // This may happen more then once. So a flag for the first time will
    // give you a sufficient Limit for Initialization
}

alternatively there is the viewWillAppear:

Another option is to have IB load the Class for you this way it is not null. And will be available in viewDidLoad:

Your MyClass will need to be a UIView.

You place a UIView (as a placeholder) into the TableViewController's View where you want it.

Then you set its class type in Interface Builder in the Identity Inspector under Custom Class.

Finally you drag a connection from the IBOutlet

@property (nonatomic, retain) IBOutlet MyClass *myClass;

to your Custom Class UIView in interface builder

Then when Interface builder initializes your class it will populate that property with a blank instance of that class.

A third option would be a lazy load property

Here is how I do lazy load

//we will take for granted that your @property is in the header
//and the following Synthesize is on the class
@synthesize myClass=_myClass; // setting instance variable to avoid confusion 
- (MyClass*) myClass
{
    if (_myClass == nil) _myClass = [[MyClass alloc] init];//Create a default instance and retain it
    return _myClass;
}

then you use self.myClass and your getter method will create one for you.

EDIT:

I added the =_myClass to the @synthesize to change the instance variable away from the property

this way using myClass will generate an error and you will be forced to use self.myClass to get the property accessor.

The Lazy Coder
  • 11,560
  • 4
  • 51
  • 69
  • I should mention I'm not using interface builder at all. – SundayMonday Sep 23 '11 at 00:25
  • 1
    well viewDidLoad should fire when the Class has been initialized and is ready for use. Without setting the property it will be null in viewDidLoad. How about a lazy loader. Ill update the example – The Lazy Coder Sep 23 '11 at 00:27
-1

Change the following line from:

@property (nonatomic, retain) MyClass *myClass;

to:

@property (nonatomic, assign) MyClass *myClass;
John Carter
  • 2,056
  • 1
  • 17
  • 16
  • Not sure that it will, but since you are creating the myClass instance outside of the class you are calling then that class isn't really the owner. Your instance may be released by the code generated with 'retain' – John Carter Sep 24 '11 at 16:04