Chapter     7

Runtime System

Objective-C has a considerable number of dynamic features, which are functionality and behaviors that are executed at runtime, rather than when the code is compiled or linked. These features are implemented by the Objective-C runtime system, and are what provides much of the power and flexibility of the language. They can be used to facilitate developing and updating programs both in real time, without the need to recompile and redeploy software, and over time, with minimal or no impact to the existing software.

Understanding how the Objective-C runtime works will help you gain a much deeper understanding of the language and how your programs are run. In this chapter, you will explore the dynamic features of the language in detail and demonstrate how you can use them in your programs.

Dynamic Features

At runtime, the Objective-C language performs many common behaviors—such as type determination and method resolution—that other languages perform during program compilation or linking, if at all. It also provides APIs that enable you to perform additional runtime operations such as object introspection and the dynamic creation and loading of code. These features are made possible through the architecture and implementation of the Objective-C runtime. In the next few sections, you’ll examine these features in more detail and learn how they are used in your programs.

Object Messaging

In OOP terminology, message passing is a style of communication used to send and receive messages between objects. Objective-C message passing (e.g., object messaging) is used to invoke methods on both classes and class instances (i.e., objects). The example in Figure 7-1 depicts the syntax for sending a message to an object/class.

9781430250500_Fig07-01.jpg

Figure 7-1. Objective-C message passing

In the message-passing expression shown in Figure 7-1, the receiver (adder) is the target of the message (i.e., the object or class), and the message itself (addend1:25 addend2:10) is composed of a selector along with any corresponding input parameters.

Object messaging is implemented as a dynamic feature—the actual type of the receiver of the message and the corresponding method to be invoked on the receiver is determined at runtime (see Figure 7-2).

9781430250500_Fig07-02.jpg

Figure 7-2. Objective-C object messaging

Figure 7-2 depicts how the Objective-C runtime implements the mapping of messages to method calls via dynamic typing and dynamic binding, both of which you’ll look at shortly. Object messaging provides tremendous flexibility with its dynamic programming features. In addition to simplifying the programming interface, these capabilities also enable the development of modular applications that can be modified and/or updated during program execution.

Because Objective-C method calls are resolved at runtime, there is a certain amount of overhead associated with dynamic binding. The Objective-C runtime system caches method calls—saving the message-to-method association in memory—to reduce this overhead.

The other consequence of runtime resolution of method calls is that the receiver is not guaranteed to be able to respond to a message. If it cannot, it will raise a runtime exception. The Objective-C language provides several features (e.g., object introspection and message forwarding) that can be used to mitigate this scenario.

To summarize, the following are the key elements of Objective-C object messaging:

  • Message: A name (the selector) and a set of parameters sent to an object/class.
  • Method: An Objective-C class or instance method that has a specific declaration comprised of a name, input parameters, a return value, and the method signature (the data type(s) for the input parameters and return value).
  • Method binding: The process of taking a message sent to a particular receiver and finding and executing the appropriate method. The Objective-C runtime performs dynamic binding of messages to method calls.

You’re probably pretty comfortable with methods, receivers, and messages because you’ve discussed these terms throughout this book, but what about selectors and method signatures? Let’s look at these in the coming paragraphs.

Selectors

In Objective-C object messaging, a selector is a text string that refers to a method and can be sent to an object or a class. The Objective-C runtime uses selectors to retrieve the correct method implementation for a target object/class. A selector is represented as a text string broken up into segments, with a colon placed at the end of each segment that is followed by a parameter:

nameSegment1:nameSegment2:nameSegment3:

This selector has three segments, each of which is followed by a colon, thereby indicating that the corresponding message has three input parameters. The number of parameters for a message corresponds to the number of segments in its selector. If a message has no parameters, the selector has one segment without a colon. Some examples of valid selectors are shown in Listing 7-1.

Listing 7-1.  Example Valid Selectors

description
description:
setValue:
sumAddend1:addend2:
sumAddend1::

In Objective-C source code, a message selector directly maps to one or more class/instance method declaration. Listing 7-2 depicts a class interface that includes a single instance method declaration.

Listing 7-2.  Calculator Class Interface

@interface Calculator : NSObject
- (int) sumAddend1:(NSInteger)a1 addend2:(NSInteger)a2;
@end

The selector for the Calculator class instance method shown in Listing 7-2 is sumAddend1:addend2:. If a Calculator object is instantiated and assigned to a variable named myCalculator, this instance method is invoked using the receiver object (myCalculator) followed by the selector with the desired input parameters, as in the following, for example:

[myCalculator sumAddend1:25 addend2:10];

OK, so this is all well and good. These examples show the role of selectors in object messaging, but you may still be wondering how all this stuff works. You probably guessed it: the Objective-C runtime! When your source code is compiled, the compiler (a component of the runtime) creates data structures and function calls that support the dynamic mapping of receiver class/objects and message selectors to method implementations. When your code is executed, the runtime library (another component of the runtime) uses this information to find and invoke the appropriate method. Later in this chapter, you’ll review the components of the Objective-C runtime in detail.

Empty Selector Segments

If you were really paying attention in the previous section, you may have noticed one of the examples in Listing 7-1 has a selector that looks a little different from the others; it’s the example sumAddend1::. For this selector, the first name segment has a text string (sumAddend1) but the second one doesn’t! Actually, a method declaration with more than one argument can have empty argument names; hence a selector with more than one segment can have empty selector segments (i.e., segments without names). Listing 7-3 extends the Calculator class interface with an instance method that includes an empty segment.

Listing 7-3.  Instance Method with Empty Selector Segment

@interface Calculator : NSObject
- (int) sumAddend1:(NSInteger)a1 addend2:(NSInteger)a2;
- (int) sumAddend1:(NSInteger)a1 :(NSInteger)a2;
@end

This instance method can be invoked with the following expression:

[myCalculator sumAddend1:25 :10]

As with any message-passing expression, each selector name segment is followed by a parameter. In a selector with one or more empty segments, the name segment is not provided but the parameter is still required, hence the message sumAddend1:25 :10 in the preceding example. You won’t normally see many method declarations with empty argument names (thus, empty selector segments), because a typo in an object message used to invoke such a method could easily lead to errors that are hard to debug.

The SEL Type

Up to this point, you have defined a selector as a text string that is part of a message in a message-passing expression; now, you’ll examine the selector type. A selector type (SEL) is a special Objective-C type that represents a unique identifier that replaces a selector value when the source code is compiled. All methods with the same selector value have the same SEL identifier. The Objective-C runtime system ensures that each selector identifier is unique. A variable of type SEL can be created using the @selector keyword.

SEL myMethod = @selector(myMethod);

So why would you create SEL variables? Well, the Objective-C runtime system (via NSObject) includes many methods that utilize variables of type SEL as parameters to dynamic methods. In addition to obtaining information about objects and classes, NSObject includes several methods for invoking a method on an object using a selector parameter. The following example uses the NSObject instance method performSelector:withObject:withObject: to invoke a method specified by the selector variable.

[myCalculator performSelector:@selector(sumAddend1::) withObject:[NSNumber numberWithInteger:25]
                         withObject:[NSNumber numberWithInteger:10]];

The @selector directive creates a selector variable at compile time. You can also create a selector at runtime with the Foundation Framework NSSelectorFromString function. In this case, the previous example is updated as follows:

 SEL selector = NSSelectorFromString(@"sumAddend1::");
 [myCalculator performSelector:selector withObject:[NSNumber numberWithInteger:25]
                         withObject:[NSNumber numberWithInteger:10]];

Method Signatures

Now that you have examined message receivers, selectors, and the SEL type, let’s look at method signatures and their role in object messaging. A method signature defines the data type(s) for the input parameters of a method along with its returned result (if any). Now you may be thinking: OK, so that’s all well and good, but why is this important when sending a message to an object? To understand that, let’s take a moment to discuss some of the internals of how the runtime system implements object messaging.

Note   In the next chapter, you are going to examine the details of the runtime system and how its dynamic behaviors (such as object messaging) are implemented. In this chapter, you focus on the runtime system architecture and not its implementation details.

The compiler translates an object message of the form [receiver message] into a (ANSI) C function call whose declaration includes the method signature. Thus, in order to generate the correct code for object messaging, the compiler needs to know both the selector value and the method signature.  Now the selector is easily extracted from the object message expression, but how does the compiler determine the method signature? After all, the message may include input parameters, but you don’t know how these types map to the actual method to be invoked because the receiver (and corresponding method) isn’t determined until runtime. Well, to determine the correct method signature, the compiler makes a guess based on the methods it can see from the declarations it has parsed so far.  If it can’t find one, or there’s a mismatch between the declarations it sees and the method that will actually be executed at runtime, a method signature mismatch can occur, resulting in anything from compiler warnings to runtime errors.

To demonstrate a scenario that could cause a method signature mismatch, let’s say that your program uses three classes whose interfaces are as shown in Listing 7-4.

Listing 7-4.  Calculator Classes Methods

@interface Calculator1 : NSObject
- (int) sumAddend1:(NSInteger)a1 addend2:(NSInteger)a2;
@end

@interface Calculator2 : NSObject
- (float) sumAddend1:(float)a1 addend2:(float)a2;
@end

@interface Calculator3 : NSObject
- (NSInteger) sumAddend1:(NSInteger)a1 addend2:(NSInteger)a2;
@end

So what happens when your code sends the message [receiver sumAddend1:25 addend2:10] where the receiver is of type id? Depending upon the interface(s) your code imports and the runtime type of the receiver (using Listing 7-4 the receiver could be of type Calculator1, Calculator2, or Calculator3), a method signature mismatch may occur. This condition can cause a variety of errors, such as stack overflow, invalid method inputs, or an invalid returned result.

This example illustrates the danger of method signature mismatch. To avoid this scenario, it’s best to try to ensure that methods with different signatures also have different names.

Using Object Messaging

Now you’ll create an example program to illustrate the use of selectors and the SEL type. 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 (choose Command Line Tool from the OS X Application selection) and click Next. In the Project Options window, specify Calculator for the Product Name, choose Foundation for the Project Type, select ARC memory management by checking the Use Automatic Reference Counting check box, and then click Next.

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 the Create button.

Next, you’ll create a Calculator class. Add the class to the project. Select New image File ... from the Xcode File menu, select the Objective-C class template, name the class Calculator (in the Subclass Of drop-down list, select NSObject), select the Calculator folder for the files location and the Calculator project as the target, and then click the Create button.

You’re done creating the program files, now let’s implement the code. Select the Calculator.h file in the navigator pane, and then add the code shown in Listing 7-5.

Listing 7-5.  Calculator Class Interface

#import <Foundation/Foundation.h>

@interface Calculator : NSObject

- (NSNumber *) sumAddend1:(NSNumber *)adder1 addend2:(NSNumber *)adder2;
- (NSNumber *) sumAddend1:(NSNumber *)adder1 :(NSNumber *)adder2;

@end

The Calculator class adds two instance methods: sumAddend1:addend2: and sumAddend1::. Each method takes two input parameters of type NSNumber * and returns an NSNumber *. Also note that the second parameter for the second method takes an empty argument name. Now select the Calculator.m file and implement the Calculator class, as shown in Listing 7-6.

Listing 7-6.  Calculator Class Implementation

#import "Calculator.h"

@implementation Calculator

- (NSNumber *) sumAddend1:(NSNumber *)adder1 addend2:(NSNumber *)adder2
{
  NSLog(@"Invoking method on %@ object with selector %@", [self className],
        NSStringFromSelector(_cmd));
  return [NSNumber numberWithInteger:([adder1 integerValue] +
                                      [adder2 integerValue])];
}

- (NSNumber *) sumAddend1:(NSNumber *)adder1 :(NSNumber *)adder2
{
  NSLog(@"Invoking method on %@ object with selector %@", [self className],
        NSStringFromSelector(_cmd));
  return [NSNumber numberWithInteger:([adder1 integerValue] +
                                      [adder2 integerValue])];
}

@end

Each method simply returns the sum of the two input parameters. The methods also log to the console the class name of the (receiver) object and a selector text string. This string is obtained using the Foundation function NSStringFromSelector.

NSStringFromSelector(_cmd)

The input parameter for this function is a variable of type SEL. So what’s this _cmd parameter and where did it come from? Well, _cmd is an implicit parameter (available in the implementation of every Objective-C method but not declared in its interface) that holds the selector of the message being sent. Thus, the expression NSStringFromSelector(_cmd) returns a text string for the selector of the method being invoked.

You’ve implemented the Calculator class, now let’s implement the main() function. Select the main.m file in the navigator pane and then update the main() function, as shown in Listing 7-7.

Listing 7-7.  Calculator main( ) Function Implementation

#import <Foundation/Foundation.h>
#import "Calculator.h"

int main(int argc, const char * argv[])
{
  @autoreleasepool
  {
    Calculator *calc = [[Calculator alloc] init];
    NSNumber *addend1 = [NSNumber numberWithInteger:25];
    NSNumber *addend2 = [NSNumber numberWithInteger:10];
    NSNumber *addend3 = [NSNumber numberWithInteger:15];
    NSLog(@"Sum of %@ + %@ = %@", addend1, addend2,
          [calc sumAddend1:addend1 addend2:addend2]);
    NSLog(@"Sum of %@ + %@ = %@", addend1, addend3,
          [calc sumAddend1:addend1 :addend3]);
  }
  return 0;
}

The main() function creates a Calculator object and uses it to add two numbers, logging the result to the output pane. When you compile and run the program, the output should look like Figure 7-3.

9781430250500_Fig07-03.jpg

Figure 7-3. Calculator program

Now let’s add code to invoke a dynamic method on an object. You’ll use the NSObject instance method performSelector:withObject:withObject:, which requires a selector input parameter. Update the main() function as shown in Listing 7-8.

Listing 7-8.  main( ) Function Using the performSelector: Method

#import <Foundation/Foundation.h>
#import "Calculator.h"

int main(int argc, const char * argv[])
{
  @autoreleasepool
  {
    Calculator *calc = [[Calculator alloc] init];
    NSNumber *addend1 = [NSNumber numberWithInteger:25];
    NSNumber *addend2 = [NSNumber numberWithInteger:10];
    NSNumber *addend3 = [NSNumber numberWithInteger:15];
    
    SEL selector1 = @selector(sumAddend1:addend2:);
    id sum1 = [calc performSelector:selector1 withObject:addend1
               withObject:addend2];
    NSLog(@"Sum of %@ + %@ = %@", addend1, addend2, sum1);
    
    SEL selector2 = NSSelectorFromString(@"sumAddend1::");
    id sum2 = [calc performSelector:selector2 withObject:addend1
               withObject:addend3];
    NSLog(@"Sum of %@ + %@ = %@", addend1, addend3, sum2);
  }
  return 0;
}

The code creates a selector for each instance method and then computes the sum of the two numbers using the NSObject performSelector:withObject:withObject: method. The first selector is created using the @selector directive, thus, at compile time. The second selector is created at runtime using the Foundation function NSSelectorFromString. The results of the method invocation are logged to the output pane. OK, this all looks good—but wait a minute, when you compile the program now, you see a couple of warnings in the main() function (see Figure 7-4).

9781430250500_Fig07-04.jpg

Figure 7-4. Calculator program selector warning

This warning, PerformSelector may cause a leak because its selector is unknown, is generated because the method will throw an exception if no method matching the selector is found, potentially causing a memory leak. You can remove this warning by adding pragma directives to the code (shown in bold in Listing 7-9).

Listing 7-9.  Pragma Directives for performSelector: Warnings

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
    SEL selector1 = @selector(sumAddend1:addend2:);
    id sum1 = [calc performSelector:selector1 withObject:addend1
               withObject:addend2];
    NSLog(@"Sum of %@ + %@ = %@", addend1, addend2, sum1);
    
    SEL selector2 = NSSelectorFromString(@"sumAddend1::");
    id sum2 = [calc performSelector:selector2 withObject:addend1
               withObject:addend3];
    NSLog(@"Sum of %@ + %@ = %@", addend1, addend3, sum2);
#pragma clang diagnostic pop

The pragma directive clang diagnostic ignored is used to disable specific compiler warnings. The syntax for this directive is

#pragma clang diagnostic ignored "DiagnosticName"

The name of the diagnostic is specified within double quotes. In this case, the diagnostic -Warc-performSelector-leaks disables compiler warnings for a potential memory leak when invoking the performSelector:withObject:withObject: method. The pragma clang diagnostic push and pragma clang diagnostic pop directives are used to save and restore the current compiler diagnostic settings. This ensures that the compiler will continue with its usual compiler options when compiling the rest of the source file. When you compile and run the program now, you should see expected output without the compiler warnings (see Figure 7-5).

9781430250500_Fig07-05.jpg

Figure 7-5. Calculator program using performSelector

Great job. Now that you’ve covered object messaging, selectors, and method signatures in detail, let’s examine the other dynamic features of the runtime.

Dynamic Typing

Dynamic typing enables the runtime to determine the type of an object at runtime; thereby letting runtime factors dictate what kind of object is to be used in your code. This is particularly beneficial when it isn’t known in advance the type of object that needs to be assigned to a variable, such as when passing arguments to methods. Objective-C supports both static and dynamic typing. When a variable is statically typed, its type is specified upon variable declaration. For example, the following statement declares that the variable myAtom is a pointer to an object of type Atom.

Atom *myAtom;

With static typing, the compiler can perform type checking at compile time and thus detect type errors prior to program execution. With dynamic typing, on the other hand, type checking is performed at runtime. Objective-C provides support for dynamic typing via the id data type. The id type is a unique Objective-C type that can hold a pointer to any type of Objective-C object, regardless of its class. The following statement declares that the variable myAtom is a pointer to an object of type id.

id *myAtom;

Hence, the variable myAtom may actually point to an object of type Atom, Hydrogen, and so forth—the actual type of the variable is determined at runtime. Dynamic typing permits associations between objects to be determined at runtime rather than forcing them to be encoded in a static design. This can make it much easier to write a single method that can handle an object from any class, rather than write a different method for each class in an application. For example, the following instance method declaration uses dynamic typing, thereby enabling the single input parameter to be an object from any class.

- (NSInteger) computeValue:(id)parameter;

Dynamic typing can be used to simplify your class interfaces because there is no need to create a different method declaration for each possible input parameter type. Dynamic typing also gives you much greater flexibility because the types used by a program can evolve during its execution and new types may be introduced without the need to recompile or redeploy. Because Objective-C supports both static and dynamic typing, you can provide different levels of type information to a method declaration; for example, the previous method declaration is modified to specify that the input parameter may be an object of any type that conforms to the NSDecimalNumberBehaviors protocol.

- (NSInteger) computeValue:(id<NSDecimalNumberBehaviors>)parameter;

Next, the method declaration is modified to specify that the input parameter is a pointer to an object of type NSNumber.

- (NSInteger) computeValue:(NSNumber *)parameter;

Finally, the method declaration is further refined to specify that the input parameter is a pointer to an object of type NSNumber that conforms to the NSDecimalNumberBehaviors protocol.

- (NSInteger) computeValue:(NSNumber<NSDecimalNumberBehaviors> *)parameter;

Objective-C also provides APIs for runtime object introspection (for example, asking a dynamically typed, anonymous object what its class is) to partially mitigate the loss of static type checks.  Introspection enables the runtime to verify the type of an object and thus validate its suitability for a particular operation. You’ll learn about object introspection later in this chapter.

Dynamic Binding

Dynamic binding is the process of mapping a message to a method at runtime, rather than at compile time. In effect, the message and the object receiving the message aren’t set until the program is running and the message is sent. Since the same method could be implemented by potentially many (receiver) objects, the exact method invoked can vary dynamically. Dynamic binding thus enables OOP polymorphism. Dynamic binding allows new objects and code to be interfaced with or added to a system without affecting existing code, therefore decreasing coupling between objects. It also enables a reduction in program complexity by eliminating the need for conditional logic to handle multiple-choice scenarios (typically implemented with a switch statement). Dynamic binding implies the use of dynamic typing. In the following code fragment, the variable atom of type id is sent the message logInfo.

id atom = [[Hydrogen alloc] initWithNeutrons:1];
[atom logInfo];

When this code executes, the runtime determines the actual type of the variable atom (via dynamic typing), and then uses the message selector (logInfo) to map the message to a corresponding instance method implementation for the receiver object atom. In this case, the variable atom resolves to the type Hydrogen *, hence the runtime searches for a Hydrogen instance method logInfo. If none is found, it searches for a corresponding instance method in its superclass. The runtime continues searching the class hierarchy until it finds the instance method (see Figure 7-6).

9781430250500_Fig07-06.jpg

Figure 7-6. Objective-C dynamic binding

Dynamic binding is an inherent feature of Objective-C and it doesn’t require any special APIs. Dynamic binding even allows the message that’s sent (the message selector) to be a variable determined at runtime.

Dynamic Method Resolution

Dynamic method resolution enables you to provide the implementation of a method dynamically. Objective-C includes the @dynamic directive, which tells the compiler that the methods associated with a property will be provided dynamically. The Apple Core Data Framework makes use of the @dynamic directive to generate efficient attribute accessor methods and relationship accessor methods for managed object classes.

The NSObject class includes the methods resolveInstanceMethod: and resolveClassMethod: to dynamically provide an implementation for a given selector for an instance and class method, respectively. You override one or both of these methods to dynamically implement instance/class methods. In the next section, you’ll learn how to use these methods by developing code to dynamically resolve a method implementation for a selector.

Providing a Method Implementation Dynamically

Now you’re going to demonstrate dynamic method resolution by updating the Calculator program to dynamically provide the implementation of a method. Start Xcode (if not already started) and open the Calculator project by selecting Open Recent image Calculator.xcodeproj from the Xcode File menu. First, you’ll update the Calculator class implementation. Select the Calculator.m file in the navigator pane and then update the Calculator implementation by overriding the resolveInstanceMethod: class method, as shown in Listing 7-10.

Listing 7-10.  Calculator Class Dynamic Method

#import <objc/runtime.h>
...
+ (BOOL) resolveInstanceMethod:(SEL)aSEL
{
  NSString *method = NSStringFromSelector(aSEL);

  if ([method hasPrefix:@"absoluteValue"])
  {
    class_addMethod([self class], aSEL, (IMP)absoluteValue, "@@:@");
    NSLog(@"Dynamically added instance method %@ to class %@", method,
          [self className]);
    return YES;
  }
  return [super resolveInstanceMethod:aSEL];
}

In Listing 7-10, an import directive for the runtime library is added to the class implementation (#import <objc/runtime.h>); this adds the runtime system APIs to your code. In the resolveInstanceMethod: class method, the runtime API class_addMethod() is used to dynamically add a function as an instance method to a class. This function takes as its input parameters the class the method will be added to, the selector for the new method, the address for the function, and an array of characters that describe the types of the arguments to the method. As you can see from Listing 7-10, the function being added as an instance method is called absoluteValue; it takes an id as its input parameter and returns an id as its result. Now let’s add the absoluteValue() function to the Calculator class implementation. Add the code shown in Listing 7-11.

Listing 7-11.  Calculator Class Dynamic Method

id absoluteValue(id self, SEL _cmd, id value)
{
  NSInteger intVal = [value integerValue];
  if (intVal < 0)
  {
    return [NSNumber numberWithInteger:(intVal * -1)];
  }
  return value;
}

Notice that the function’s input parameters include the implicit parameters self and _cmd; self is the receiving object and _cmd is the selector for the method.

Now let’s update the main() function to test dynamic method resolution. Select the main.m file from the navigator pane and update the function, as shown in Listing 7-12.

Listing 7-12.  Calculator Class main() Function with Dynamic Method

#import <Foundation/Foundation.h>
#import "Calculator.h"

int main(int argc, const char * argv[])
{
  @autoreleasepool
  {
    Calculator *calc = [[Calculator alloc] init];
    NSNumber *addend1 = [NSNumber numberWithInteger:-25];
    NSNumber *addend2 = [NSNumber numberWithInteger:10];
    NSNumber *addend3 = [NSNumber numberWithInteger:15];
    
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
    SEL selector1 = @selector(sumAddend1:addend2:);
    id sum1 = [calc performSelector:selector1 withObject:addend1
               withObject:addend2];
    NSLog(@"Sum of %@ + %@ = %@", addend1, addend2, sum1);
    
    SEL selector2 = NSSelectorFromString(@"sumAddend1::");
    id sum2 = [calc performSelector:selector2 withObject:addend1
               withObject:addend3];
    NSLog(@"Sum of %@ + %@ = %@", addend1, addend3, sum2);
    
    SEL selector3 = NSSelectorFromString(@"absoluteValue:");
    NSLog(@"Invoking instance method %@ on object of class %@",
          NSStringFromSelector(selector3), [calc className]);
    id sum3 = [calc performSelector:selector3 withObject:sum2];
    NSLog(@"Absolute value of %@ = %@", sum2, sum3);
#pragma clang diagnostic pop
  }
  return 0;
}

The code dynamically creates a selector for the new method and then invokes an instance method using the selector. Dynamic resolution causes the method to be added to the runtime and invoked, returning its result. The result is then displayed in the output pane. Now save, compile, and run the updated Calculator program and observe the messages in the output pane (see Figure 7-7).

9781430250500_Fig07-07.jpg

Figure 7-7. Objective-C dynamic method resolution

As you can observe in the output pane, the new method (absoluteValue:) is added to the Calculator class by the Objective-C runtime when the message using the selector absoluteValue: is dynamically invoked via the method performSelector:withObject:. As shown by this example, dynamic method resolution can be used to add methods to classes at runtime.

Dynamic Loading

Dynamic loading enables an Objective-C program to load both executable code and resources, as they are needed, instead of having to load all program components at application startup. The executable code (which is linked prior to loading) can contain new classes that become integrated into the runtime image of the program. This lazy-loading of program code and resources improves overall performance by placing lower memory demands on the system. This also enhances program extensibility because new software can be added dynamically without change to an existing program. Apple provides the bundle mechanism to support the dynamic loading of software on the iOS and OS X platforms.

A bundle is a software delivery mechanism. It consists of a directory with a standardized hierarchical structure that holds executable code and the resources used by that code. A bundle can contain executable code, images, sound files, or any other type of code or resource. It also contains a runtime configuration file called the information property list (Info.plist). Bundles define the basic structure for organizing the code and resources associated with your software; they come in several types:

  • Application bundle: An application bundle manages the code and resources associated with a process (e.g., a program).
  • Framework bundle: A framework bundle manages a dynamic shared library and its associated resources, such as header files. Applications can link against one or more frameworks, for example the Foundation Framework.
  • Loadable bundle: A loadable bundle (also referred to as a plug-in) is a type of bundle that an application can use to load custom code dynamically.

The Foundation Framework NSBundle class can be used to manage bundles. An NSBundle object represents a location in the file system that groups code and resources that can be used in a program. Listing 7-13 uses an NSBundle object to dynamically load the path of an application’s information property list named Info.plist.

Listing 7-13.  Dynamically Loading a Property List File

NSBundle *bundle = [NSBundle mainBundle];
NSString *bundlePath = [bundle pathForResource:@"Info" ofType:@"plist"];

As mentioned, bundles can be used to dynamically load executable code. Listing 7-14 uses an NSBundle object to dynamically load a framework bundle and then create an instance of a class from the framework.

Listing 7-14.  Dynamically Loading a Framework Object

NSBundle *testBundle = [NSBundle bundleWithPath:@"/Test.bundle"];
id tester = [[[bundle classNamed:@"Tester"] alloc] init];

Introspection

The Foundation Framework NSObject APIs include numerous methods for performing object introspection. These methods dynamically query the runtime for the following types of information:

  • Information about methods
  • Testing object inheritance, behavior, and conformance

Because Objective-C defers much of its behaviors to the runtime, rather than compile or link time, object introspection can be a critical capability to help you avoid runtime errors such as message-dispatch errors, erroneous assumptions of object equality, and other problems.

The following statement uses the NSObject isKindOfClass: method to test whether the receiver of a message is an instance of the Calculator class or an instance of any class that inherits from the Calculator class.

BOOL isCalculator = [myObject isKindOfClass: [Calculator class]];

The next statement checks whether an object responds to a selector; that is, whether it implements or inherits a method that can respond to a specified message.

BOOL responds = [myObject respondsToSelector:@selector(sumAddend1::)];

The next statement checks whether an object conforms to a given protocol.

BOOL conforms = [myObject conformsToProtocol:@protocol(MyProtocol)];

The following statement obtains the method signature for a selector.

NSMethodSignature *signature = [myObject methodSignatureForSelector:@selector(sumAddend1::)];

This is just a sample of the NSObject methods for performing object introspection. In Part 3 of this book, you’ll look at the NSObject APIs in more detail.

Roundup

In this chapter, you examined in detail the features of the Objective-C runtime and reviewed the key components of the runtime system architecture. To recap, the following are the key takeaways:

  • Objective-C message passing (e.g., object messaging) is used to invoke methods on both classes and objects. Object messaging is implemented as a dynamic feature—the actual type of the receiver of the message and the corresponding method to be invoked on the receiver is determined at runtime.
  • A message-passing expression consists of a receiver (the target object/class of the message) and the message itself. The message is composed of a selector along with any corresponding input parameters.
  • A selector is represented as a text string broken up into segments, with a colon placed at the end of each segment that is followed by a parameter. A selector with more than one segment can have empty selector segments (i.e., segments without names).
  • A selector type (SEL) is a special Objective-C type that represents a unique identifier that replaces a selector value when the source code is compiled. A variable of type SEL can be created using the @selector keyword or with the Foundation Framework function NSSelectorFromString().
  • A method signature defines the data type(s) for the input parameters of a method along with its returned result (if any). Method signature mismatch—where the compiler can’t determine the appropriate method for an object message, or there’s a mismatch between the declarations it sees and the method that will actually be executed at runtime—can result in anything from compiler warnings to runtime errors. To avoid this scenario, it’s best to try to ensure that methods with different signatures also have different names.
  • Dynamic typing enables the runtime to determine the type of an object at runtime, thereby letting runtime factors dictate what kind of object is to be used in your code. Objective-C provides support for dynamic typing via the id data type.
  • Dynamic binding is the process of mapping a message to a method at runtime, rather than at compile time. Dynamic binding enables OOP polymorphism and allows new objects and code to be interfaced with or added to a system without affecting existing code, therefore decreasing coupling between objects.
  • Dynamic method resolution enables you to provide the implementation of a method dynamically. Objective-C includes the @dynamic directive, which tells the compiler that the methods associated with a property will be provided dynamically. The NSObject class includes the methods resolveInstanceMethod: and resolveClassMethod: to dynamically provide an implementation for a given selector for an instance and class method, respectively.
  • Dynamic loading enables an Objective-C program to load both executable code and resources, as they are needed, instead of having to load all program components at application startup. Apple provides the bundle mechanism to support the dynamic loading of software on the iOS and OS X platforms. The Foundation Framework NSBundle class can be used to manage bundles. An NSBundle object represents a location in the file system that groups code and resources that can be used in a program.
  • The Foundation Framework NSObject APIs include numerous methods for performing object introspection. These APIs dynamically query the runtime for information about methods. They also perform tests for object inheritance, behavior, and conformance.

Now that you know the features of the runtime system and how they can be used in your programs, let’s examine the runtime system’s architecture and implementation. When you’re ready, turn the page to begin!

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.16.75.165