Using Introspection in iPhone

Objective – C allows dynamic binding: a feature that makes it possible to postpone decisions about what classes we are dealing until runtime. In order to make this kind of decision however, we need to be able to determine exactly what class or classes we’re dealing with. It would not do (for example) to send a method to change the text of an object that didn’t implement that method. In this blog, we’re going to explore Introspection as a way to find information about classes at runtime.

Open Xcode, choose “Create a new Xcode project,” select the Single View Application template, and click Next. Name the project “Introspection,” and choose options as shown here:

Click Next, choose a location to save the project, and click Create.

When the app template has been created, open the ViewController.zib file, and drag controls to the view as shown below:

Note that the large white square is a UIView object containing eight labels. All other controls are on the main view.

Next, open the ViewController.h file, and make changes as shown below:

#import <UIKit/UIKit.h>

@interface ViewController : UIViewController

(IBAction)test1Pressed:(UIButton *)sender;
(IBAction)test2Pressed:(UIButton *)sender;
(IBAction)test3Pressed:(UIButton *)sender;
(IBAction)test4Pressed:(UIButton *)sender;

@end

We’re only declaring action methods for the four UIButton objects. We don’t need IBOutlets in this app; we’re going to find controls in the view by inspecting the properties of the objects inside the main view using introspection. Wire up the four actions above to the UIButtons in the Interface Builder.

After the buttons are wired up to their corresponding methods, open the ViewController.m file and add the method definitions shown here:

(IBAction)test1Pressed:(UIButton *)sender
{
    for (UIView *aView in self.view.subviews) {
        if ([aView isMemberOfClass:[UILabel class]]) {
            aView.backgroundColor = [UIColor redColor];
        }
    }
}

(IBAction)test2Pressed:(UIButton *)sender
{
    for (UIView *aView in self.view.subviews) {
        if ([aView isKindOfClass:[UIView class]]) {
            aView.backgroundColor = [UIColor blueColor];
        }
    }
}

(IBAction)test3Pressed:(UIButton *)sender
{
    for (id aView in self.view.subviews) {
        if ([aView canPerformAction:@selector(setText:) withSender:sender]) {
            [aView setText:@"Hello!"];
        }
    }
}

(IBAction)test4Pressed:(UIButton *)sender
{
    for (UIView *aView in self.view.subviews) {
        if ([aView isMemberOfClass:[UIView class]]) {
            for (UILabel *aLabel in aView.subviews) {
                aLabel.backgroundColor = [UIColor greenColor];
            }
        }
    }
}

Add these methods immediately after the @implementation line in the .m file. Run the application now, and observe the results of pressing each button.

The first test looks at all subviews of the main view. It then uses isMemberOfClass on each one to determine if each view is a UILabel. The isMemberOfClass method returns YES if an object is an instance of one specific class. So if this method finds a UILabel that is a subview of the main view, it will set its background color to red:

The label objects inside the white square UIView do not get their background color set to red. This is because these labels are subviews of a subview, not direct subviews of the main view.

The second test looks for all subviews of the main view that are descended from UIView. This is accomplished by calling isKindOfClass. Since all controls are descendents of UIView, this method will change the background color of all of the subviews to blue:

Again, the background color of the labels internal to the white square view is unchanged, since the default background color of a UILabel is transparent.

Test 3 searches all the subviews of the main view for objects that have a setText method. It accomplishes this by using the canPerformAction method. Since the only subview of the main view that has a setText: method is the UILabel at the top, the method changes the text to “Hello!”:

Test 4 shows one way to get to the label objects in the white square view. First, we must find a subview of the main view that is a member of the UIView class. Then, we look for subviews of that view that are UILabels, and set their background color to green:

Introspection is useful when we have a large number of similar controls and we want to find a particular control by looking at some attribute (for example, a tag). It is also useful when we’re using inheritance. We might want all subclasses of some class to perform a method, but only if that method is defined. Since we might not know what objects will be performing the method at compile time, we can see that introspection is an extremely powerful tool.

Leave a Reply

Your email address will not be published. Required fields are marked *