Introduced
OBJECTIVE-C has postponed many decisions from a cheap period and link period to a run-time period. Whenever possible, it does a lot of things dynamically. This means that it requires not only a compiler, but also a runtime system to execute the compiled code. For objective-c, this runtime system is like an operating system, enabling objective-c to work properly.
This article explores the NSObject class, and how the OBJECTIVE-C program interacts with the runtime system.
By reading this article, you should understand how Objective-c's runtime system works and how to use it. Although for writing a cocoa program, you may not need to understand this article.
The organization of this article
This article includes the following chapters:
- Version and platform of the runtime system
- Interacting with the runtime system
- Send Message
- Dynamic Method Parsing
- Message forwarding
- Type encoding
- Declared properties
See
The Objective-c run-time guide describes the data structures and functions of the OBJECTIVE-C Runtime library. Your program can use these interfaces to interact with the OBJECTIVE-C runtime system. For example, you can add classes and methods to a class that has already been loaded, or get a list of class definitions.
Version and platform of runtime
There are many different versions of the OBJECTIVE-C runtime on different platforms.
Past and modern versions
The OBJECTIVE-C runtime has 2 versions-modern and past versions. The modern version was introduced in OBJECTIVE-C 2.0, including a series of new features. The previous version of the programming interface was described in "Objective-c 1 runtime Reference", the modern version of the programming interface in "Objective-c runtime Reference".
The most notable new feature is that the instance variable is "Non-fragile" in modern Runtime:
- In previous versions, if you changed the layout of instance variables in a class, you had to recompile the subclass of the class;
- In modern versions, if you change the layout of an instance variable of a class, you do not need to recompile the subclass of the class;
In addition, the modern runtime supports instance variable compositing of the declared properties (see the OBJECTIVE-C programming Language for details).
Platform
iphone programs and 64-bit programs running in OSX v10.5 and above use the modern version of runtime, and other programs (32-bit programs for OSX desktops) use the previous version of runtime.
Interacting with the runtime
OBJECTIVE-C program and runtime interaction can be divided into 3 levels: through Objective-c source code, through the foundation framework of the NSObject class method, by directly calling the runtime function.
Objective-c Source Code
Normally, the runtime system runs automatically behind the screen, and you only use it by writing and compiling objective-c code. When you compile code with OBJECTIVE-C classes and methods, the compiler creates data structures and function calls that implement the language's dynamic characteristics. These data structures capture the information in the class, category, and protocol declarations, including our defining a class and protocols in the OBJECTIVE-C programming Language The classes and protocol objects discussed in, selector, instance variable templates, and other content in the source code. The main runtime functions are those that can send messages, which we'll focus on in the messaging section, which is called by the source code's message expression.
NSObject method
Most objects in cocoa are subclasses of nsobject, so most objects inherit their methods. (One notable exception is the Nsproxy class, see the section "message forwarding" for details.) Therefore, the behavior of its methods can also be inherited by each object and by each instance of a class. In some cases, however, the NSObject class simply defines a template and does not provide the code.
For example, the NSObject class defines a description instance method that returns a string that describes the contents of the class. It is primarily used for debugging--gdb print-object to print the string returned by the description method. NSObject does not know the contents of this class, so it returns only the name and address of the object, and NSObject subclasses can return more details. For example, the description method of Nsarray Returns a list of the descriptions of the objects it includes.
Some nsobject methods simply query the runtime's information. These methods allow the object to be introspective. An example is the class method, which asks the object for its class, as well as the Iskindofclass: Methods and Ismemberofclass: methods, which detect where an object is located in the class hierarchy, Respondstoselector: Detects whether an object responds to a message, Conformstoprotocol: Detects whether an object implements a specific protocol method. These methods give the object the ability to introspect.
Runtime method
Runtime system is a dynamic shared library, it has a public interface, its header file is located in the/USR/INCLUDE/OBJC folder, there is a collection of methods and data structures. When you write objective-c code, there are many ways to use C code to reproduce what the compiler does. Other methods form the basis of functionality, which is derived through the NSObject class method. Some runtime methods are useful when writing objective-c programs. "Objective-c Runtime Reference" describes all these methods.
News
This chapter will explain how message passing translates into Objc_msgsend method invocation, how you can refer to a method by name, and how you can use the Objc_msgsend method, and how to avoid dynamic binding when you need it.
Objc_msgsend method
In Objective-c, the message is not bound to the method implementation until run time. The compiler will put a message expression
[receiver message]
Translates to a call to the message function--objc_msgsend. The Objc_msgsend function takes the recipient of the message and the method name in the message as arguments. Any arguments passed in the message are also handled by Objc_msgsend:
...)
Message functions are custom-made for dynamic binding:
- It first finds the procedure (method implementation) that the selector refers to. Because the same method can be implemented by different classes, the process that it eventually finds depends on the recipient's class.
- It then invokes the process, passing it to the object it receives (a pointer to its data), and the other parameters that the method requires.
- Finally, it takes the return value of the procedure as its own return value.
Note: The compiler generates a call to the message function, and you should not call it directly in your own code.
The key to sending messages is the structure that the compiler generates for each class and object, each of which consists of 2 basic elements:
- A pointer to the superclass.
- The sub-publication of the class. The entries in this table are the mappings of method selectors and method addresses for specific classes. For example Setorigin:: The selector is associated with the Setorigin:: The implementation of the address, display selector and display of the address of the association, and so on.
When a new object is created, the memory is allocated and its instance variables are initialized. Here is a pointer to its class structure, which is the ISA pointer that enables the object to access its class, and through this class it can access all its parent classes.
Note: Strictly speaking, the ISA pointer is not part of the language itself, but it is required for collaboration between the object and the Objective-c runtime system. The object needs to be equal to each field of the objc_object structure. However, you rarely need to create your own root object, and objects that inherit from NSObject and Nsproxy automatically have an ISA variable.
When a message is sent to an object, the message function looks for the method selector with the ISA pointer in its class's message sub-publication. If the selector is not found, Objc_msgsend will follow a pointer to the parent class and try to find the selector in its method sub-publication, if it fails objc_msgsend continues up the inheritance system until it reaches the NSObject class. Once the selector is found, the message function invokes the method and passes the data structure of the receiving object to it.
This is how the runtime chooses the method implementation, which is how the method is dynamically bound to the message in object-oriented programming terminology.
To speed up the processing of messages, the runtime caches the selectors and the used method addresses. Each class has a separate cache that stores the methods inherited from the parent class and the selectors of its own unique method. Before a search is split, message routing checks the cache of the class of the object that receives the message (theoretically, if a method is used, it may be reused). If the method selector is in the cache, then this message processing is only a little slower than the method call. Once a program has been running for a long time to build this cache, almost all of the messages will find a cached method. As the program runs, the cache dynamically grows to accommodate the new message.
Using hidden parameters
When Objc_msgsend discovers the process of implementing a method, it invokes the process and passes the message to all its arguments, and also passes 2 hidden arguments:
- Receiving objects
- Method Selector
These parameters give the method the explicit information about and two parts of the implementation, which is said to be hidden because they do not have explicit declarations in the source code of the method. They are inserted into the implementation at compile time.
Although these parameters are not explicitly declared, the source code can still refer to them (as if it were able to reference the receiving object instance variable). A method uses self to refer to the receiving object, using _cmd to refer to the selector. In the following example, _cmd refers to the selector of the Stange method, and the self references the object that receives the Stange message.
- strange
{
id target = getTheReceiver();
SEL method = getTheMethod();
if ( target == self || method == _cmd )
return nil;
return [target performSelector:method];
}
Self is more useful, and the method definition makes it possible for an instance variable of the receiving object to be available.
Gets the address of a method
The only way to avoid dynamic binding is to get the address of the method and call it directly, as if it were a function. In some rare cases, you might want a specific method that executes many times in order but avoids the overhead of sending messages each time the method executes, which may be the only appropriate method.
Using the Methodforselector: Method defined in the NSObject class, you can query a function pointer that implements a method, and then you invoke the function with this pointer. Methodforselector: The function pointer returned by the method must be carefully converted to the appropriate type, including the return value and parameters.
The following example shows how the implementation of setfilled: How the function of a method is called:
void (*setter)(id, SEL, BOOL);
int i;
setter = (void (*)(id, SEL, BOOL))[target
methodForSelector:@selector(setFilled:)];
for ( i = 0 ; i < 1000 ; i++ )
setter(targetList[i], @selector(setFilled:), YES);
The first two arguments passed to the function are the Receive object (self) and the method selector (_cmd). These parameters are hidden in the method syntax, but must be explicit when the method is called as a function.
Use Methodforselector when sending messages: avoiding dynamic binding can save a lot of time, but this savings is only noticeable when a particular message is repeated many times, as shown in the For loop above.
Note that Methodforselector: is provided by the Cocoa runtime system and is not a feature of the Objective-c language.
Dynamic Method Parsing
This chapter explains how to provide implementations dynamically for a method.
Dynamic Method Parsing
On some occasions you might want to dynamically give a method an implementation, such as the @dynamic keyword for objective-c language:
@dynamic propertyName;
It tells the compiler that this method will be dynamically provided.
You can implement Resolveinstancemethod: and Resolveclassmethod: Provides methods dynamically for an instance object and class, respectively.
A objective-c method is a C function that includes at least 2 parameters--self and _cmd. You can use the function Class_addmethod to add a function to a class as a method of the class. For the following functions:
void dynamicMethodIMP(idself, SEL _cmd) { // implementation ....}
You can use Resolveinstancemethod: add it to the class as a method, for example:
@implementation MyClass
+ (BOOL)resolveInstanceMethod:(SEL)aSEL
{
if (aSEL == @selector(resolveThisMethodDynamically)) {
class_addMethod([self class], aSEL, (IMP) dynamicMethodIMP, "v@:");
return YES;
}
return [super resolveInstanceMethod:aSEL];
}
@end
The forwarding method (also called message forwarding) and dynamic message parsing are strongly correlated. The class has the opportunity to parse the method dynamically before the forwarding mechanism is started. If you use Respondstoselector: or instancesrespondtoselector: First, the dynamic parser can provide an imp for the selector. If you implement Resolveinstancemethod: But only want specific selectors to be forwarded through the forwarding mechanism, you can return no for those selectors that you do not want to forward.
Dynamic loading
The OBJECTIVE-C program can load and link new classes and categories (Catogery) at run time. The new code will fit nicely into the program as well as classes and classes loaded at the beginning of the program.
Dynamic loading is used to do a lot of different things. For example, those modules in System preferences are dynamically loaded.
In a cocoa environment, dynamic loading is often used to allow programs to be customized. Your program can load some modules written by others when it runs--as if interface Builder loads a custom toolbox and OSX System Preferences to load a custom preference module.
The loaded program can extend the functionality of the original program. They provide these modules in a way that you allow but do not anticipate and define its behavior. You define the framework, they implement the code.
Although there are methods to perform dynamic loading of objective-c modules in the Mach-o file (objc_loadmodules, defined in objc/objc-load.h), Cocoa's NSBundle class provides a more convenient interface for dynamic binding-- It is object-oriented and integrates with related classes. See the description of the NSBundle class of the foundation framework reference for the NSBundle class and its usage. For information about Mach-o, see OS X ABI mach-o File Format Reference.
Message forwarding
An error occurs when sending a message to an object that it cannot handle, however, before the declaration is faulted, the runtime system gives the object receiving the message
An opportunity to process the message.
Forward
When you send a message to an object that it cannot process, the runtime sends it a forwardinvocation: message with a unique parameter ——— a Nsinvocation object before it declares an error. The Nsinvocation object encapsulates the message itself and the parameters of the message band.
You can implement the Forwardinvocation: method to give a default response to the message, or to avoid errors in other ways. As it literally does, forwardinvocation: It is commonly used to forward messages to other objects.
For the scope and intent of forwarding, imagine the following scenario: First, suppose you design an object that responds to a negotiate message, and you want its response to include the response of another object. You can do it easily: you only need to pass the negotiate message to another object in the Negotiate method you implement.
Further, suppose you want your object's response to the negotiate message to be implemented in other classes. One way to do this is to make your object's class inherit from other classes, but this is sometimes impossible, such as your class and the class that implements negotiate in different inheritance systems.
Although your class cannot inherit the Negotiate method, you can still "borrow" the Negotiate method by implementing a method in which you simply need to pass the message to that class object.
- (id)negotiate
{
if ( [someOtherObject respondsTo:@selector(negotiate)] )
return [someOtherObject negotiate];
return self;
}
This is a bit of a hassle, especially if you have a lot of messages to forward to other objects. You have to implement a way to cover every method that you want to "borrow" from other objects. However, to overwrite each message, it is not possible to handle each case, and it depends on the runtime time, which may change with the methods and classes implemented later.
Forwardinvocation: Provides a special solution that is dynamic rather than static. When an object cannot respond to a message because it lacks a method to match the message selector, the runtime sends it a forwardinvocation: message. Each object inherits the Forwardinvocation: Method from the NSObject class. However, the NSObject version of this method only calls the Doesnotrecognizeselector: method. By rewriting this method of the NSObject version, you can take advantage of the forwardinvocation: the time the message is provided to forward your message to other objects.
To forward a message, forwardinvocation: What needs to be done is:
1. Decide where the message should flow;
2. Pass the original parameter to it
This message must be able to be invokewithtarget: send:
- (void)forwardInvocation:(NSInvocation *)anInvocation
{
if ([someOtherObject respondsToSelector:
[anInvocation selector]])
[anInvocation invokeWithTarget:someOtherObject];
else
[super forwardInvocation:anInvocation];
}
The return value of the forwarded message is returned to the original sender. All types of return values can be passed to the sender, including IDs, structs, and double-precision floating-point numbers.
Forwardinvocation: Acts like a distribution center for unrecognized messages, distributing messages to different recipients. or a conversion center that sends all the messages to the same destination. It can translate one message into another, and can "devour" the message, without responding and without errors. Forwardinvocation: You can also combine many messages into a single response. Forwardinvocation: How the implementation depends on the executor. It provides a mechanism that makes it possible to link objects in a forwarding chain.
Note: The message will only be processed if the recipient has not called the existing method forwardinvocation. This means that if you want your object to forward negotiate messages to other objects, then it cannot have a negotiate method. If it does, the message will never reach Forwardinvocation:.
For more information about message forwarding and invocation, please participate in the description of the Nsinvocation class in the Foundation framework reference.
Forwarding and multiple inheritance
Forwards impersonation inheritance, which can be borrowed from several multiple inheritance effects. An object responds to a message by forwarding, as if it were borrowed from another object or inherited a method implementation.
In, a Warrior object forwards the negotiate message to a diplomat object, and the Warrior object is like a diplomat object, which looks like it can respond to a negotiate message. It can respond in all cases (although it is a diplomat object).
The object that forwards the message thus inherits this method from the two branches of the inheritance system-its own branch and the branch where the message responder resides. In the example above, it seems as if the Warrior object inherits from the diplomat class and its own superclass.
Message forwarding provides most of the features you want to get from multiple inheritance. However, there is a significant difference between the two: multiple inheritance combines different functions into one object, which focuses on large, multi-layered objects. Forwarding focuses on assigning different abilities to different objects. It breaks down the problem into small objects, connecting them in a way that is transparent to the sender of the message.
Proxy Object
Forwarding not only mimics multiple inheritance, but also makes it possible to develop a lightweight object that represents or overrides more entity objects. The agent stands at the angle of the other object and passes the message to it.
Proxies make it possible to forward messages to a remote object, and parameters can be copied and retrieved correctly. However, it does not copy the function of the remote object, it only gives the remote object a local address, and it can receive messages through this local address.
Other categories of proxy objects are also possible. For example, you have an object that has a large amount of data-it may create a complex image or read the contents of a file on disk. Creating this object can be time consuming, so you might prefer to create it later-when it is needed or when the system resources are idle. At the same time you need a placeholder for this object so that other objects in the program will work correctly.
In this case, you may start not to create a complete object, but rather a lightweight proxy. This object can handle things on its own, such as answering questions about data, but its main function is to be a placeholder for this large object and to forward the message at the appropriate time. When the Forwardinvocation: Method of the proxy object receives a message to another object, it is created to ensure the existence of the object when the object does not exist. All messages sent to this large object pass through this proxy, and the objects and proxies are equivalent.
Forwarding and inheritance
Although forwarding resembles inheritance, the NSObject class will never confuse the two. Respondstoselector:and Iskindofclass: These two methods exist only in the inheritance system and are not present in the forwarding chain. For example, if a Warrior object responds to the Negotiate method, the code is:
if ( [aWarrior respondsToSelector:@selector(negotiate)] ) ...
The answer is no, although it can receive negotiate messages and react by forwarding messages to the Diplomat object.
In many cases, negation is the right answer. But it may not be the case. If you use forwarding to create a proxy object or to extend a class, the forwarding mechanism is as transparent as the inheritance mechanism. If you want your objects to behave as if they really inherit the behavior of the object you are forwarding to, you need to re-implement Respondstoselector: Methods and Iskindofclass: Methods to include your forwarding mechanism:
- (BOOL)respondsToSelector:(SEL)aSelector
{
if ( [super respondsToSelector:aSelector] )
return YES;
else {
/* Here, test whether the aSelector message can *
* be forwarded to another object and whether that *
* object can respond to it. Return YES if it can. */
}
return NO;
}
In addition to Respondstoselector: and Iskindofclass: Methods, the Instancesrespondtoselector method should also map the forwarding mechanism. Conformstoprotocol: The method should also be added to the list. Similarly, if an object forwards all the methods it receives, it should have a method Methodsignatureforselector: the ability to return a description of a method, for example, if an object can forward a message to its proxy, It may implement Methodsignatureforselector as follows: Method:
-(nsmethodsignature*) Methodsignatureforselector: (SEL) Selector
{
nsmethodsignature* signature = [Super Methodsignatureforselector:selector];
if (!signature) {
Signature = [Surrogate methodsignatureforselector:selector];
}
return signature;
}
You might consider putting these forwarding algorithms in private code, so that you have all the methods, Forwardinvocation: Already included, call it.
Note: This is a high-level technology that only applies to situations where no other solution is available. He should not be a substitute for inheritance. If you have to use this technique, make sure that you have fully understood the behavior of the class you are forwarding and the class that you are forwarding to.
The methods mentioned in this section are described in the description of the NSObject class in the foundation framework reference. For more information on Invokewithtarget: Refer to the nsinvocation in the Foundation framework reference.
Type encoding
(All forms, temporarily not translated, original address: Objective-c Runtime Programming Guide)
Property declaration
When the compiler encounters a property declaration (see the attribute declaration in the OBJECTIVE-C programming Language), it produces some descriptive metadata for that class, category, or protocol. There are several ways you can access these metadata, which can query properties by name of the class or protocol, get the type of the property, and copy the properties of the property. The list of property declarations applies to each class and protocol.
Property Types and methods
The property structure defines the handle of the attribute descriptor.
typedefstruct objc_property *Property;
You can use the Class_copypropertylist and Protocol_copypropertylist methods to get a list of properties for a class, category, or protocol:
*class_copyPropertyListint*outCount*protocol_copyPropertyList*protoint*outCount)
For example, given the following class declaration:
@interface Lender : NSObject { float alone;}@propertyfloat alone;@end
You can get a list of properties like this:
id LenderClass = objc_getClass("Lender");
unsigned int outCount;
objc_property_t *properties = class_copyPropertyList(LenderClass, &outCount);
You can use Property_getname to get the property name:
constcharproperty)
You can use methods Class_getproperty and Protocol_getproperty to get a reference to a class or a property of the protocol's specified name:
constcharconstcharBOOLBOOL isInstanceProperty)
You can use the Property_getattributes method to get the name of the property and a string of @encode type. For details on the encoding type string, refer to the "Type Code" section for details, please refer to the attribute type string and the attribute type description example.
constcharproperty)
Therefore, you can print a list of properties for a class with this code:
id LenderClass = objc_getClass("Lender");
unsigned int outCount, i;
objc_property_t *properties = class_copyPropertyList(LenderClass, &outCount);
for (i = 0; i < outCount; i++) {
objc_property_t property = properties[i];
fprintf(stdout, "%s %s\n", property_getName(property), property_getAttributes(property));
}
Property type String
You can use the Property_getattributes method to get the name of the property, the @encode type string, and other properties of the property.
The string starts with T, then the @encode type and comma, and finally the name of the V and the instance variable. In this, the attributes are specified by these descriptors:
Table 7-1 Attribute type encoding
(The form is no longer translated, the original address: Objective-c Runtime Programming Guide)
Objective-c Runtime Programming Guide Chinese Translation