IOS Runtime principle and usage, iosruntime Principle

Source: Internet
Author: User

IOS Runtime principle and usage, iosruntime Principle
Runtime Introduction

Because Objc is a dynamic language, it always finds a way to delay some decision work from compilation connection to runtime. That is to say, only the compiler is not enough. A runtime system is also required to execute the compiled code. This is the meaning of the existence of the Objective-C Runtime system. It is a cornerstone of the entire Objc Runtime framework.

RunTime is short for RunTime. OC is the runtime mechanism, the most important of which is the message mechanism. For the C language, function calls determine which function to call during compilation. For OC functions, they are a dynamic call process. During compilation, they cannot decide which function to call, the corresponding function can be called only when the function is running.

Runtime is basically written in C and sink. It can be seen that Apple has made great efforts for the efficiency of dynamic systems. You can go to the open source code maintained by Apple here. Both Apple and GNU maintain an open-source runtime version.

Runtime-related header files

The usr/include/objc folder in ios sdk contains the following files:

These are header files related to the runtime. The main functions used are defined in the message. h and runtime. h files. Message. h mainly contains some functions for sending messages to objects, which is the underlying implementation of OC object method calls.

You need to import files for use, such:

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

Runtime. h is the most important file during runtime, which contains the methods for running operations. It mainly includes:

Operation Object Type Definition
/// An opaque type that represents a method in a class definition. A type represents a Method in the class definition, typedef struct objc_method * Method; // An opaque type that represents an instance variable. the typedef struct objc_ivar * Ivar variable representing the instance (object); // An opaque type that represents a category. typedef struct objc_category * Category; // An opaque type that represents an Objective-C declared property. represents the attribute typedef struct o declared by OC Bjc_property * objc_property_t; // Class represents a Class. It defines typedef struct objc_class * Class in objc. h; struct objc_class {Class isa OBJC_ISA_AVAILABILITY; # if! _ OBJC2 _ Class super_class comment; const char * name comment; long version comment; long info comment; long instance_size comment; struct objc_ivar_list * ivars comment; struct objc_method_list ** methodLists comment; struct objc_cache * cache OBJC2_UNAVAILABLE; struct objc_protocol_list * protocols OBJC2_UNAVAILABLE; # endif} OBJC2_UNAVAILABLE;

The definitions of these types completely decompose a class and abstract each part of the class definition or object into a type, which is very convenient for operating a class attribute and method.OBJC2_UNAVAILABLEThe marked attributes are not supported by Ojective-C 2.0, but they can be obtained using the response function. For example, if you want to obtain the name attribute of the Class, you can obtain the attributes as follows:

Class classPerson = Person. class; // printf ("% s \ n", classPerson-> name); // you cannot get the name using this method because OBJC2_UNAVAILABLEconst char * cname = class_getName (classPerson ); printf ("% s", cname); // output: Person

 

Function Definition
  • Generallyobject_Start
  • Generallyclass_Start
  • The method for operations on methods of classes or objects is generallymethod_Start
  • Generallyivar_Start
  • GenerallyStart with property _Start
  • Generallyprotocol_Start

Based on the prefix of the above functions, you can get a general understanding of the hierarchical relationship.

Forobjc_The method at the beginning is the final manager of runtime, which can obtain the loading information of classes in the memory, the list of classes, associated objects and associated attributes, and other operations.

For example, use runtime to print the classes loaded in the current application.

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {    unsigned int count = 0;    Class *classes = objc_copyClassList(&count);    for (int i = 0; i < count; i++) {        const char *cname = class_getName(classes[i]);        printf("%s\n", cname);    }}

 

The runtime application sends messages.

The essence of method calling is to let an object send messages.

Objc_msgSend. Only objects can send messages. Therefore, it starts with objc.

Before using the message mechanism, you must import # import <objc/message. h>

Simple use of message mechanism:

 

// Create the person object Person * p = [[Person alloc] init]; // call the object method [p eat]; // essence: Let the object send the message objc_msgSend (p, @ selector (eat); // call the class method: Two // The first method is to call [Person eat] by class name; // The second method is to call [[Person class] eat] through a class object; // call a class method with the class name. The underlying layer automatically converts the class name into a class object call // nature: let the class Object send the message objc_msgSend ([Person class], @ selector (eat ));

 

We can use clang to view the CPP Code generated by the Code.

The final code requires re-compiling the current Code, using the xcode compiler, clang

Clang-rewrite-objc main. m to view the final generated code

Exchange Method

Scenario for implementing the exchange method: You have created a functional method that is referenced multiple times in the project. When the project requirements change, you must use another function to replace this function, the requirement is that the old project is not changed (that is, the implementation of the original method is not changed ).

You can write a new method (meeting the new requirements) in the class classification, and then exchange the implementation of the two methods. In this way, the project is improved without changing the project code, but with the addition of new code.

The implementation of the two methods is generally written in the load method of the class, because the load method will be loaded once before the program runs, and the initialize method will be called when the class or subclass is used for the first time, it is called multiple times when there is a classification.

@ Implementation ViewController-(void) viewDidLoad {[super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. // requirement: Provides the imageNamed method with the function to determine whether the image is loaded successfully each time. // Step 1: first define a classification, define a method that can load images and print them + (instancetype) imageWithName :( NSString *) name; // Step 2: by switching the implementation of imageNamed and imageWithName, you can call imageWithName and indirectly call the implementation of imageWithName. UIImage * image = [UIImage imageNamed: @ "123"] ;}@ end @ implementation UIImage (Image) // call + (void) when loading classification to memory) load {// exchange Method // obtain the imageWithName Method address Method imageWithName = class_getClassMethod (self, @ selector (imageWithName :)); // obtain the imageWithName Method address Method imageName = class_getClassMethod (self, @ selector (imageNamed :)); // exchange method address, which is equivalent to the exchange implementation method method_exchangeImplementations (imageWithName, imageName);} // The system method imageNamed cannot be rewritten in a category, this will overwrite the system functions, and super cannot be called in classification. // The image can be loaded and printed + (instancetype) imageWithName :( NSString *) name {// call imageWithName here, which is equivalent to calling imageName UIImage * image = [self imageWithName: name]; if (image = nil) {NSLog (@ "loading empty images") ;}return image ;}@ end

 

Associated object of class \ object

The Correlated object is not used to add attributes or member variables to the class or object (because it cannot be obtained through ivarList or propertyList after the association is set), but to add a related object to the class, it is usually used to store class information, such as an array of Storage Class Attribute lists, to facilitate future dictionary-to-model conversion.

Method 1: Add attributes to a category

 

@ Implementation ViewController-(void) viewDidLoad {[super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. // Add the attribute name NSObject * objc = [[NSObject alloc] init]; objc to the NSObject class dynamically. name = @ ""; NSLog (@ "% @", objc. name) ;}@ end // define the associated keystatic const char * key = "name"; @ implementation NSObject (Property)-(NSString *) name {// obtain the associated value based on the associated key. Return objc_getAssociatedObject (self, key);}-(void) setName :( NSString *) name {// The first parameter: the object to which the association is added // The second parameter: the associated key is obtained through this key // The third parameter: the associated value // The fourth parameter: the associated policy objc_setAssociatedObject (self, key, name, OBJC_ASSOCIATION_RETAIN_NONATOMIC);} @ end

 

Method 2: add an associated object to the object.

For example, alertView. Generally, the value is passed and the tag attribute of alertView is used. We want to pass more parameters to the alertView Proxy:

/*** Delete click ** @ param recId shopping cart ID */-(void) shopCartCell :( BSShopCartCell *) shopCartCell didDeleteClickedAtRecId :( NSString *) recId {UIAlertView * alert = [[UIAlertView alloc] initWithTitle: @ "" message: @ "confirm to delete this baby" delegate: self cancelButtonTitle: @ "cancel" otherButtonTitles: @ "OK", nil]; // pass multiple parameters objc_setAssociatedObject (alert, "suppliers_id", @ "1", callback); objc_setAssociatedObject (alert, "warehouse_id ", @ "2", OBJC_ASSOCIATION_RETAIN_NONATOMIC); alert. tag = [recId intValue]; [alert show];}/*** confirm the delete operation */-(void) alertView :( UIAlertView *) alertView clickedButtonAtIndex :( NSInteger) buttonIndex {if (buttonIndex = 1) {NSString * warehouse_id = values (alertView, "warehouse_id"); NSString * suppliers_id = values (alertView, "suppliers_id "); NSString * recId = [NSString stringWithFormat: @ "% ld", (long) alertView. tag] ;}}

 

objc_setAssociatedObjectDescription of method parameters:

Dynamic Addition Method

Development Scenarios: if there are many class methods, loading classes to the memory is also resource-consuming, You need to generate a ing table for each method, you can use dynamic to a class, add a solution.

Classic interview questions: Do you have used javasmselector? In fact, I mainly want to ask if you have added dynamic methods.

Easy to use:

@ Implementation ViewController-(void) viewDidLoad {[super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. person * p = [[Person alloc] init]; // default person. The eat method is not implemented and can be called by the performSelector, but an error is reported. // If you add a method dynamically, no error will be reported [p performSelector: @ selector (eat)];} @ end @ implementation Person // void (*)() // The default method has two implicit parameters: void eat (id self, SEL sel) {NSLog (@ "% @", self, NSStringFromSelector (sel ));} // when an object calls an unimplemented method, it calls this method for processing and transmits the corresponding list of methods. // It can be used to determine whether the unimplemented method is the method we want to dynamically add + (BOOL) resolveInstanceMethod :( SEL) sel {if (sel = @ selector (eat )) {// Add the eat method dynamically // The first parameter: add the method to the class // The second parameter: add the method ID // The third parameter: function implementation (function address) of the add method // fourth parameter: function type, (Return Value + parameter type) v: void @: Object-> self: indicates SEL-> _ cmd class_addMethod (self, @ selector (eat), eat, "v @:");} return [super resolveInstanceMethod: sel];} @ end

 

Dictionary-to-model KVC implementation

KVC: assign values of all values in the dictionary to the attributes of the model. This requires the Key in the dictionary and the corresponding value must be found in the model. If no value is found, an error is returned. The basic principles are as follows:

// KVC principle: // 1. traverse all keys in the dictionary and find out the corresponding attributes in the model. [dict enumerateKeysAndObjectsUsingBlock: ^ (id _ Nonnull key, id _ Nonnull value, BOOL * _ Nonnull stop) {// 2. search for the corresponding attribute KVC // key: source value: From instant note // [item setValue: @ "from instant note" forKey: @ "source"] [item setValue: value forKey: key] ;}];

 

However, in actual development, it is not necessary to obtain all the values from the dictionary. Therefore, we can rewrite the forUndefinedKey method in KVC to avoid error handling.

// Rewrite the system method? 1. I want to add additional functions to the system method. 2. if you do not want to implement the system method, // The system will call this method if it cannot be found. The error "-(void) setValue :( id) value forUndefinedKey :( NSString *) key {}" is returned {}

 

In addition, we can use the runtime method. Let's reverse the KVC principle by traversing the value of the model and taking the value from the dictionary.

// Ivar: The member variable starts with an underscore. // Property: Property + (instancetype) modelWithDict :( NSDictionary *) dict {id objc = [[self alloc] init]; // runtime: take the corresponding value from the dictionary based on the attributes in the model and assign a value to the model attributes. // 1. get the key of all member variables in the model // obtain the member variable of the class // count: Number of member variables unsigned int count = 0; // obtain the member variable array Ivar * ivarList = class_copyIvarList (self, & count); // traverse all member variables for (int I = 0; I <count; I ++) {// obtain the member variable Ivar ivar = ivarList [I]; // obtain the member variable name NSString * iva RName = [NSString stringwithuf8string: ivar_getName (ivar)]; // obtain the member variable type NSString * ivarType = [NSString stringwithuf8string: ivar_getTypeEncoding (ivar)]; // @ \ "User \"-> User ivarType = [ivarType stringByReplacingOccurrencesOfString: @ "\" "withString: @" "]; ivarType = [ivarType stringByReplacingOccurrencesOfString: @ "@" withString: @ ""]; // obtain the key NSString * key = [ivarName substringFromIndex: 1]; // go to the dictionary. Find the corresponding value // key: user value: NSDictionary id value = dict [key]; // second-level conversion: determines whether the value is a dictionary. If yes, if ([value isKindOfClass: [NSDictionary class] &! [IvarType hasPrefix: @ "NS"]) {// converts a dictionary to a model userDict => User Model // The model to which the dictionary is converted. // retrieves the Class modelClass = NSClassFromString (ivarType ); value = [modelClass modelWithDict: value];} // assign an if (value) {[objc setValue: value forKey: key];} return objc;} to the model attribute ;}

 

This is the basic code.

What is runtime?

1> OC is a fully dynamic language, and everything in OC is implemented based on Runtime.
The OC code compiled at ordinary times is actually converted into the C language code of runtime during the program running process. runtime is regarded as the person behind OC.
For example:

OC :[[Person alloc] init]runtime :objc_msgSend(objc_msgSend("Person" , "alloc"), "init")

 

2> runtime is a relatively low-level pure C language API. It belongs to one C language library and contains many underlying C language APIs.
3> the implementation of runtimeAPI is developed in C ++ (The implementation files in the source code are all mm) and is an open-source framework of apple.

 
 

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.