views:

37

answers:

2

I'm having a problem with either typecasting or object scope. I'm getting an uncaught exception:

// Create the object here so that it's scope is outside the `if` statement, right?
searchTableViewController *newViewController;

if (rowSelected) {
    // Typecast the object to a searchTableViewController
    (searchTableViewController *)newViewController ;
    // Initialize and Allocate 
    newViewController = [[searchTableViewController alloc] initWithSearchBar:NO grouped:NO];
}else{
    // Typecast the global object to a personViewController
    (personViewController *)newViewController;
    // Initialize and Allocate
    newViewController = [[personViewController alloc] init];
}

// Act on the object: create a data model object and store it as a property, etc.
newViewController.myDataModel = [[dataModel alloc] initWithSelected:selectedField delegate:newViewController];

I have 2 similar ViewControllers searchTableViewController and personViewController. So I want to be able to instantiate either one or the other, using the same name, that way the rest of my code can act on the viewController using common properties and such.

This is causing a Terminating due to uncaught exception and regardless it seemed like the wrong way to do it anyway. I need help either in the typecasting department or I need help understanding how to declare the scope of these objects properly so I can work with them in and outside of the if statement.

Update


The easiest thing would be for me to write my code as follows. How can I declare, allocate and instantiate an object within an if statement and then have it accessible outside?:

if (rowSelected) {
    searchTableViewController *newViewController = [[searchTableViewController alloc] initWithSearchBar:NO grouped:NO];
}else{
    personViewController *newViewController = [[personViewController alloc] init];
}

// This will probably give an error since newViewController is not in the proper scope.
newViewController.myDataModel = [[dataModel alloc] initWithSelected:selectedField delegate:newViewController];
+2  A: 

You want to declare your newViewController as the common subclass of the two objects you're possibly allocating. Probably UIViewController.

UIViewController *newViewController;

if (rowSelected) {
    // Initialize and Allocate 
    newViewController = [[SearchTableViewController alloc] initWithSearchBar:NO grouped:NO];
}else{
    // Initialize and Allocate
    newViewController = [[PersonViewController alloc] init];
}

And the cast operation isn't doing anything when you use it in-place like that.


Edit - if both of those classes have common properties, like dataModel, then you can avoid warnings by creating a common base class that derives from UIViewController and which contains those properties. You'd then change the declaration of your view controller variable in the first line above to match the intermediate base class.

Later Edit — if you don't want to create an intermediate base class, you can do the following (newViewController still must be declared as a UIViewController):

if([newViewController respondsToSelector:@selector(setMyDataModel:)]) {
    DataModel *dataModel = [[dataModel alloc] initWithSelected:selectedField delegate:newViewController];
    [newViewController performSelector:@selector(setMyDataModel:) withObject:dataModel];
}
Seamus Campbell
Ah. That sounds great. I'll try out your first suggestion.
Andrew
Also, class names should be *PascalCase*
Matt Williamson
You are correct; my code was a copy-paste of the original post. Corrected.
Seamus Campbell
I thought they were supposed to be camelCase. Is anything supposed to be camelCase in Objective-C, or is it all PascalCase, ideally?
Andrew
Variable names are camelCase. Class names PascalCase.
Seamus Campbell
So when I declare it as a `UIViewController` and get rid of the typecasting I get an `error: request for member 'myDataModel' in something not a structure or union` while trying to set the myDataModel property on the last line. I assume that's because `myDataModel` is not a property of UIViewController. FYI, personViewController is a subclass of ABUnknownPersonViewController which is a sub-class of UIViewController. And searchTableViewController is a subclass of UIViewController. So it sounds like the only solution is the intermediate base class that Seamus spoke of.
Andrew
see additional edit above for an alternate solution.
Seamus Campbell
Great, Seamus. Thank you for the detail!
Andrew
A: 

RE: Your Edit

id newViewController;

if (rowSelected) {
    newViewController = [[searchTableViewController alloc] initWithSearchBar:NO grouped:NO];
}else{
    newViewController = [[personViewController alloc] init];
}

// This will probably give an error since newViewController is not in the proper scope.
newViewController.myDataModel = [[dataModel alloc] initWithSelected:selectedField delegate:newViewController];
Matt Williamson
This won't work, since personViewController isn't directly related to searchTableViewController.
Seamus Campbell
Ah I see what you mean. I changed it to `id` which means any type. Check my edit above.
Matt Williamson
You'll still need to change `newViewController.myDataModel` to a `-performSelector` call. But it's probably a good idea to back minimally up the inheritance tree, so you can get away with property accesses without compiler complaint.
Seamus Campbell
Yes, or we can make a protocol.
Matt Williamson