Objective-C: can you test an uninitialized pointer

2019-05-21 11:27发布

I'm trying to do something along the lines of:

in some object A:

UITableVIewController *viewController;
[someObjectB setupView: viewController];

in some object B:

-(void) setupView:(UIViewController*)view
{
    if the pointer passed in is of type UITableViewController then ...
    say, for instance with: [[view class] isKindOfClass [UITableViewController class]]
    or: [view isKindOfClass [UITableViewController class]]
}

However, I have two problems: First, the fact that the parameter enters the method in B as UIViewController which is a superclass of UITableView (which I'm doing because this method will also configure other types of view controllers). So I'm wondering, if I pass in UITableViewController to a method parameter of type UIViewController, isKindOfClass still does dynamic binding right? That is, the runtime checks the type of the content, regardless of the static type of the pointer?

Second, notice I did not initialize the pointer in object A before passing it to B. I actually want B to initialize it. But then buy question becomes, how do I test the static pointer type on an uninitialized pointer? That is, inside B: [view isKindOfClass [UITableViewController class]] gives me a BAD_EXEC. My guess, because I'm obviously trying to access a jul pointer. So is there a way to still test based on the pointer static type declaration?

ADDENDUM: (cause I added it as a comment instead, sorry)

Let me rephrase that part of the question in more specific terms: I want obj B to create a variable of type UITableViewController, pass it to A and have A realize it's been given a pointer to UITableViewController, so that it can actually initialize this pointer to a specific subclass of UITableViewController and then obj B pass that now initialized object to some other object.

My reasoning behind wanting to do this is the following scenario: I have a navigationController-based app. It's got a root controller with buttons and depending which button is pressed, the root controller instantiates and pushes table controllers for different tables of a database and, in turn, when clicking on a row of those tables, the tables instantiate and push a detailed view controller.

So far so good, but because of the nature of the project, I want to keep the whole navigationcontroller instantiate-push chain agnostic to the fact that there's a database and that the tables and detailed-view controllers are actually specific subclasses of UITableViewController and UIViewController, respectively.

This means the flow would be:

1- On click, root controller passes generic UIViewController to my custom main controller.

2- This main controller realizes it's been passed a generic table, initializes it to a specific subclass defined by me and does a few things to provide it with data from the sql database.

3 - The root controller, not caring whether the generic UIViewController is a table or not or even a subclass of UIViewController, merely pushes it to the navigationController.

The idea, also, is that then I don't need to include headers for the table subclasses in the root controller. Only the custom main controller knows about these.

So, at the end of the day, the way I see of doing it is by being able to determine what is the type of the pointer being passed, regardless of whether this pointer is nil, points to garbage or points to an actual object. Meaning:

UIVIewController* a -> static type declaration, determined at compile time.

a = [some subclass of UIVIewController alloc] init]; -> dynamic type of the pointer's content, determined at run time, probably by following links through activation records.. similar to "virtual" in C++

3条回答
The star\"
2楼-- · 2019-05-21 12:02

As others have pointed out, what you are asking for is impossible. When you pass a variable to a method, it only sends the value, not the type. Also, the runtime has no knowledge of a variable's type within a method. Any variable type handling is done by the compiler. The runtime can only know the class of an object which a variable holds.

As an alternative, if you can change the signature of your method, you could add a parameter which indicates the type of object to create. The calling method still doesn't need to know the exact type of the object, just whether it is a table view controller or not. Here is an example which uses an enumeration to hold the different types.

// header

enum ViewControllerType {
    ViewControllerNormal = 0,
    ViewControllerTableView
};
- (void)setupView:(UIViewController **)view ofType:(enum ViewControllerType)type;

// implementation

- (void)setupView:(UIViewController **)view ofType:(enum ViewControllerType)type {
    if(!view) {
        NSLog(@"setupView:ofType: received nil pointer");
        return;
    }
    switch(type) {
        case ViewControllerNormal:
            // create normal UIViewController type
            break;
        case ViewControllerTableView:
            // create UITableViewController type
            break;
        default:
            NSLog(@"setupView:ofType: received unknown type: %i",type);
            return;
    }
}

Used like:

UIViewController *view;
[creatorObject setupView:&view ofType:ViewControllerNormal];
// or
UITableViewController *tableView;
[creatorObject setupView:&tableView ofType:ViewControllerTableView];
查看更多
兄弟一词,经得起流年.
3楼-- · 2019-05-21 12:02

In answer to your first question, yes - Objective C will determine the class type of your object at runtime (dynamically). In answer to your second question, any method call dispatched to a nil object is silently ignored, so none of your class checks will actually work. You should not get a BAD_EXEC however, unless you:

  • Initialized the object previously and released it without setting to nil, resulting in garbage at its memory address
  • Passed in an uninitialized automatic variable that was created by the compiler for you
  • For whatever reason, pointed it at some nonsensical address in memory.

* EDIT *

Added a scenario in which you would get a BAD_EXEC, and also, see the link below for a couple of gotchas in testing for class equality, particularly with the isKindOfClass method.

In Objective-C, what is the equivalent of Java's "instanceof" keyword?

查看更多
Deceive 欺骗
4楼-- · 2019-05-21 12:16

Objective-C is a pass-by-value language, just like C. You can't initialize viewController in your object A with the code you have there. Initialize it, pass its address to setupView:, and then initialize it as necessary:

UITableVIewController *viewController = nil;
[someObjectB setupView:&viewController];

and:

-(void)setupView:(UIViewController**)view
{
   if (*view == nil)
      // initialize it  - *view = [[blah alloc] init] or whatever
   else
       ...

}
查看更多
登录 后发表回答