Using the Runtime APIs

Using the Runtime APIs

Now you’ll create a program that uses the runtime APIs to dynamically create a class and a class instance, and then dynamically add a variable to the instance.

In Xcode, create a new project by selecting New image Project … from the Xcode File menu. In the New Project Assistant pane, create a command-line application. In the Project Options window, specify RuntimeWidget for the Product Name, choose Foundation for the Project Type, and select ARC memory management by selecting the Use Automatic Reference Countingcheck box. Specify the location in your file system where you want the project to be created (if necessary, select New Folder and enter the name and location for the folder), uncheck the Source Control check box, and then click theCreate button.

Now select the main.m file and add the code shown in Listing 9-9.

Listing 9-9.  Using NSBundle to Load a Bundle

#import <Foundation/Foundation.h>
#import <objc/runtime.h>
#import <objc/message.h>

// Method implementation function for the display selector
static void display(id self, SEL _cmd)
{
  NSLog(@"Invoking method with selector %@ on %@ instance",
        NSStringFromSelector(_cmd), [self className]);
}

int main(int argc, const char * argv[])
{
  @autoreleasepool
  {
    // Create a class pair
    Class WidgetClass = objc_allocateClassPair([NSObject class], "Widget", 0);
     
    // Add a method to the class
    const char *types = "v@:";
    class_addMethod(WidgetClass, @selector(display), (IMP)display, types);
     
    // Add an ivar to the class
    const char *height = "height";
    class_addIvar(WidgetClass, height, sizeof(id), rint(log2(sizeof(id))),
                  @encode(id));
     
    // Register the class
    objc_registerClassPair(WidgetClass);
     
    // Create a widget instance and set value of the ivar
    id widget = [[WidgetClass alloc] init];
    id value = [NSNumber numberWithInt:15];
    [widget setValue:value forKey:[NSString stringWithUTF8String:height]];
    NSLog(@"Widget instance height = %@",
          [widget valueForKey:[NSString stringWithUTF8String:height]]);

    // Send the widget a message
    objc_msgSend(widget, NSSelectorFromString(@"display"));

    // Dynamically add a variable (an associated object) to the widget
    NSNumber *width = [NSNumber numberWithInt:10];
    objc_setAssociatedObject(widget, @"width", width,
                             OBJC_ASSOCIATION_RETAIN_NONATOMIC);
     
    // Retrieve the variable's value and display it
    id result = objc_getAssociatedObject(widget, @"width");
    NSLog(@"Widget instance width = %@", result);
  }
  return 0;
}

Functionally, this code does the following:

  • Define a method implementation function
  • Create and register a class
  • Create an instance of the class
  • Dynamically add a variable to the instance

Next, you’ll examine the code and we’ll discuss how it implements this functionality.

Defining a Method Implementation

As you saw in Chapter 8, an Objective-C method is simply a C-language function that takes at least two arguments: self and _cmd. Immediately after the import statements, the code in Listing 9-9 defines a function that you’ll use to add a method to a class.

// Method implementation function for the display selector
static void display(id self, SEL _cmd)
{
  NSLog(@"Invoking method with selector %@ on %@ instance",
        NSStringFromSelector(_cmd), [self className]);
}

Creating and Registering a Class

To dynamically create a class with the runtime APIs, you must perform the following steps:

  1. Create a new class and metaclass.
  2. Add methods and instance variables to the class (if any).
  3. Register the newly created class.

This functionality is implemented with the following code from Listing 9-9.

// Create a class pair
Class WidgetClass = objc_allocateClassPair([NSObject class], "Widget", 0);
     
// Add a method to the class
const char *types = "v@:";
class_addMethod(WidgetClass, @selector(display), (IMP)display, types);
     
// Add an ivar to the class
const char *height = "height";
class_addIvar(WidgetClass, height, sizeof(id), rint(log2(sizeof(id))),
              @encode(id));
     
// Register the class
objc_registerClassPair(WidgetClass);

The runtime class_addMethod function takes as arguments the class to which the method should be added, the selector that specifies the name of the method being added, the function that implements the method, and a character string—known as type encodings—that describes the types of arguments and the return value of the method. Each possible type is represented by a type code; for example, the character “v” is the code for a void@” is the code for an object (whether a pointer to an Objective-C class or the id type)and “:” is the code for the SEL type. The complete list of type encodings are specified in the Apple Objective-C Runtime Programming Guide. The codes in the types parameter of the class_addMethod must be arranged in a defined order: the first code is for the return type, the second code is for method’s implicit self parameter (the idtype), the third code is for the type of the method’s implicit _cmd parameter (theSEL type), and the remaining codes are for the types of each of the explicit parameters of the method. Thus for the display method, the correspondingtypes array has a value of “v@:.”

Creating a Class Instance

The code creates an instance of the dynamically added class, sets its instance variable to a value, and then invokes the instance method. It also logs messages to the output pane to show the instance variable value and the method invocation.

Dynamically Adding a Variable to a Class Instance

Objective-C does not provide the capability for adding instance variables to an object; however, a feature of the runtime—associated objects—can be used to effectively mimic this functionality. An associated object is an object that is attached to a class instance, referenced by a key. This can be used in a variety of scenarios; for example, with Objective-C categories that don’t permit instance variables. When you create an associated object, you specify the key mapped for the association, the memory management policy for the associated object, and its value. The following code from Listing 9-9 demonstrates use of the runtime APIs for associated objects.

// Dynamically add a variable (an associated object) to the widget
NSNumber *width = [NSNumber numberWithInt:10];
objc_setAssociatedObject(widget, @"width", width,
                         OBJC_ASSOCIATION_RETAIN_NONATOMIC);
     
// Retrieve the variable's value and display it
id result = objc_getAssociatedObject(widget, @"width");
NSLog(@"Widget instance width = %@", result);

The runtime APIs include an enum that lists the possible values for the memory management policy. In this policy, OBJC_ASSOCIATION_RETAIN_NONATOMIC, assigns a nonatomic strong reference to the associated object. This is similar to creating a property with the attributes nonatomic, strong. When you compile and run the RuntimeWidget program, you should observe the messages in the output pane shown in Figure 9-12.

9781430250500_Fig09-12.jpg

Figure 9-12RuntimeWidget program output

This example demonstrates how to use the runtime APIs to dynamically create classes, class instances, and associated objects. Please refer to the Apple Objective-C Runtime Reference for the complete guide to the runtime APIs.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值