IOS-RunTime, no longer just heard

I. RunTime profile

RunTime runtime. OC is the run-time mechanism, that is, the run-time mechanism, the most important is the message mechanism.

For the C language, the function of the call at compile time will decide which function to call, if the call is not a function of the wrong.
for the OC language, which belongs to the dynamic invocation process at compile time and really can’t decide which function to call, only in real time operation according to the name of the function to find the corresponding function to call. In the compilation phase, OC can call any function, even if this function is not implemented, as long as the statement will not be wrong.

Two. RunTime message mechanism

The message mechanism is the most important mechanism in the runtime, and the invocation of any method in OC is essentially the sending of messages. The use of
operation, sending messages need to import the &lt framework; objc/message.h> and xcode5, apple does not recommend the use of the underlying method, if you want to use to run when the need to close the strict inspection of the call to objc_msgSend, msg to NO BuildSetting-> search.

Take a look at the example method called the underlying implementation

Person *p = [[Person alloc] init]; [p eat]; / / the bottom will be converted into //SEL: number, implementation method according to the number you can find the corresponding method. [p performSelector:@selector (eat)]; send a message //performSelector essence is to run, do things, who call who (objc_msgSend P, @selector (eat)); / / objc_msgSend with parameters (P, @selector (eat:), 10);

Class method call

Will / / is the essence of class into the class object initialization method is in fact a class object. [Person eat]; / / Person said only a class name, not a real object. As long as the method must object to call. / / RunTime calls the same method, method is to call the class object, so the need to obtain the class object, and then use the class object to call the method. Class personClass = [Persion class]; [[Persion class] performSelector:@selector (eat)]; / / object sending message objc_msgSend (personClass, @selector (eat));

@selector (SEL): is a SEL method selector. The main function of SEL is to quickly find the function pointer of the corresponding method by the method name, and then call the function. SEL itself is a type of Int address, the address stored in the name of the method.
for a class. Each method corresponds to a SEL. Therefore, a class can not exist in the same name of the 2 methods, even if the parameter type is different, because SEL is generated according to the method name, the same method name can only correspond to a SEL.

When running the bottom to send a message to the
of each class has a list of methods of Method List, this kind of method to save all of the incoming SEL, according to the numbering method for finding ways of mapping the equivalent of value – key. Then find the way to achieve. To achieve the method to achieve. As shown in the figure.

The underlying implementation of sending messages in IOS-RunTime, no longer just heard

So how do you find the corresponding method dynamically?
first we know that all classes are inherited from the NSObject class, there is a Class pointer in NSObjcet isa.

Typedef struct *Class objc_class; @interface < NSObject> {Class isa OBJC_ISA_AVAILABILITY;}

We came to the objc_class view, which contains some basic information about the class.

Struct objc_class {Class isa; / / super_class / / Class to metaclass; const char *name to its parent class; / / class name long version; / / version information class, initialize the default is 0, you can modify, read the long info through the runtime function class_setVersion and class_getVersion; / / some identifying information, such as CLS_CLASS (0x1L) said the class class, which contains the object method and member variables; CLS_META (0x2L) said the metaclass, which contains the method; long instance_size; / / an instance of the class variable size (including inherited from the superclass instance variables); struct objc_ivar_list *ivars; / / storage variables for each member of objc_method_list **methodLists struct. Info; / / and some flags, such as CLS_CLASS (0x1L), the storage of As a method, such as CLS_META (0x2L), then struct objc_cache storage method; *cache; / / pointer methods recently used, for increased efficiency; struct objc_protocol_list *protocols; / / storage of this kind of compliance with the agreement}

Here we take the example of the eat p method to see how the specific message is sent to dynamically find the corresponding method.

  1. Examples of methods [p eat]; the bottom call [p performSelector:@selector (eat)]; method, the compiler in the code into objc_msgSend (P, @selector (eat));
  2. In the objc_msgSend function. First of all, through the P isa pointer to find the corresponding P class. In the Class to go through the SEL in the cache to find the corresponding function method, if found by the method function pointer to jump to the corresponding function to perform.
  3. If cache is not found. Go to methodList to find. If you can find, will be added to the method cache, in order to facilitate the next search, and through the method pointer to jump to the corresponding function to perform.
  4. If methodlist is not found, then go to the superClass search. If you can find, will be added to the method cache, in order to facilitate the next search, and through the method pointer to jump to the corresponding function to perform.

Three. Use the RunTime exchange method:

When the system comes with the function of the method is not enough, the need to bring the system to extend some of the features, and maintain the original function, you can use the RunTime exchange method.
here to achieve image add pictures, automatically determine whether the image is empty, if it is empty to remind the picture does not exist.
method 1: use classification

(nullable * UIImage) + xx_ccimageNamed: (NSString * name) {/ / loading picture if the picture does not exist or a reminder of abnormal UIImage *image = [UIImage imageNamed:name]; if (image = = Nil) {NSLog (@ "picture does not exist");} return image;}

Disadvantages: every time you need to import the header file, and if the project is relatively large, the method used before all need to change.

Methods: two essential RunTime switching method
switching method is to realize the exchange of two methods, namely, the replacement of xx_ccimageNamed and imageName to call the xx_ccimageNamed method, is to call the imageNamed method

Well, first you need to know where to exchange the method, because the exchange only needs to be done once, so in the classification of the load method, when the classification of the exchange method can be.

(void + load) {/ / two / / get the method to exchange the access method using Method / / class: get a accept method which: / / SEL method to obtain the number, according to the SEL will be able to find the corresponding class method. Method imageNameMethod = class_getClassMethod ([UIImage class], @selector (imageNamed:)); / / get the second methods Method xx_ccimageNameMrthod = class_getClassMethod ([UIImage class], @selector (xx_ccimageNamed:)); / / exchange two method implementation method, method two. Method_exchangeImplementations (imageNameMethod, xx_ccimageNameMrthod); / / IMP is the abbreviation of implementation: representation. }

Exchange method internal implementation:

  1. According to the SEL method number found in the Method method, the two methods are found
  2. The realization of the exchange method. As shown in Figure:
    IOS-RunTime, no longer just heard
    exchange method internal implementation

Note: when the exchange method xx_ccimageNamed method can not call the imageNamed method, because the imageNamed method is called essentially equivalent to call the xx_ccimageNamed method will cause the circular reference cycle of death.

RunTime also provides a method for obtaining an object method and a method implementation.

The realization of class_getMethodImplementation / / acquisition method (< #__unsafe_unretained, < Class cls#> #SEL name#> / / class_getInstanceMethod) to acquire the object method (< #__unsafe_unretained Class cls#> < #SEL, name#>)

At this point, when the call to the imageNamed: method will call the xx_ccimageNamed: method, add pictures for the image, and to determine whether the image exists, if not, then the picture does not exist.

Four. Dynamic addition method

If a class method is very large, many of these methods may not be used for a while. While loading the class method to memory, it is necessary to generate mapping table for each method, but also more expensive resources. At this point you can use RunTime dynamic add method

Dynamic add a method to a class, which is equivalent to lazy loading mechanism, the class is not used in many ways, then do not load, and so on when used to load method.

Dynamic add method:
first we do not realize the object method, when the call performSelector: method, then the dynamic loading method.
here to create the same Person class, the use of performSelector: call Person class object eat method.

Person *p = [[Person alloc]init]; / / when did not realize the method call P, dynamic loading method of [p performSelector:@selector (eat)];

The compile time error is not reported, only when the program is running error, because the Person class does not implement the eat method, when the class of Method List to find the eat method, wrong can not find eat.

IOS-RunTime, no longer just heard
error message: not sent to instance by selector

And when you can’t find the corresponding method, you will come to the interception call, and can’t find the method before the procedure of the call.
calls the + (BOOL) resolveInstanceMethod: (SEL) sel method when an object method is not called.
calls the + (BOOL) resolveClassMethod: (SEL) sel method when the class method is not implemented.

First, we went to API to look at Apple’s instructions, the search Dynamic Method Resolution came to dynamic analysis.

IOS-RunTime, no longer just heard
Dynamic Method Resolution

Dynamic Method Resolution API has been explained very clearly, we can realize the method of resolveInstanceMethod: or resolveClassMethod: method, dynamic method to instance method or class adding method and Realization method.

Therefore, the two methods can be used to know which methods are not implemented, thus dynamically adding methods. Parameter sel means no implementation.

A objective – C method is ultimately a C function, by default any method has two parameters.
self: Method caller _cmd: call method number. We can use the function class_addMethod to add a method and Implementation for the class.

Here to follow the example of API, dynamic P instance to add eat object

(BOOL) + resolveInstanceMethod: (SEL SEL) {/ / eat add dynamic method / / first judge sel is not the eat method can also be transformed into a string comparison. If (SEL = = @selector (eat)) {/ * * the first parameter: cls: to which class adding method of second parameters: SEL name: add method No. third parameters: IMP, imp: function method and method of entrance, the function name can be different (suggestions and methods were the same fourth parameters: types:) method type, need to use a specific symbol, refer to the API / class_addMethod (self, SEL, eat (IMP), "[email protected]"); / / return YES return YES after return [super resolveInstanceMethod:sel];}};

Focus on the class_addMethod method

Class_addMethod (__unsafe_unretained Class CLS, SEL name, IMP imp, const char *types)

Four parameters in class_addMethod. First, the two parameters are better understood, the focus is on the third, the four parameters.

  1. CLS: which class to add to the method, here to add a method to the Person class, self that represents Person.
  2. SEL Name: number of add methods. Because there is only one way to add dynamic, and before the judge to determine the SEL is the eat method, so you can use sel.
  3. IMP imp: means that the realization of the function, the function name can be different from the method name (recommended with the same method name) need to achieve this function. Each method is the default with two implicit parameters
    self: Method caller _cmd: call the method of the label, you can write or write. Void eat (ID self, SEL _cmd) {/ / NSLog content (@ “% @% @ the method of dynamic realization, self, NSStringFromSelector (_cmd));}
  4. Types: represents a method type that requires a specific symbol. The examples provided by the system are “[email protected]:” we came to API to see “[email protected]:” what type of method is specified.
    IOS-RunTime, no longer just heard
    Objective-C type encodings
    can be seen from the figure of V -> void said no return value
    @ -> object ID
    said: -> method parameter selector SEL

So far has completed the P instance of the dynamic eat method to add. Output
when P calls the eat method

IOS-RunTime, no longer just heard
P calls the eat method when output

Dynamic method to add a parameter
if there are parameters of the method, the need for the realization of the method and the class_addMethod method within the type parameters to make some changes.
method: because the C language function, so the object parameter type can only be replaced by ID.
method type parameters: because the addition of a ID parameter, so the method type should be “[email protected]: @”
look at the code

(BOOL) + resolveInstanceMethod: (SEL SEL) {if (SEL = = @selector (eat:)) {class_addMethod (self, SEL, AAAA (IMP), "[email protected]: @"); return YES; return [super resolveInstanceMethod:sel]}}; void AAAA (ID self, SEL _cmd, ID Num) {/ / NSLog content (@ "% @% @ the realized method of dynamic parameters, self, NSStringFromSelector,% @" (_cmd), Num);}

Call eat: function

Person *p = [[Person alloc]init]; [p performSelector:@selector (eat:) withObject:@ "xx_cc"];

Output as

IOS-RunTime, no longer just heard
P calls the eat: method when output

Five. RunTime dynamically add attributes

Use RunTime to add attributes to the class of the system, first of all need to understand the relationship between objects and attributes.

The relationship between IOS-RunTime, no longer just heard
objects and attributes

When an object initialized the attributes of name nil, assigns them is actually the name attribute pointing to a string of memory storage, the object attributes with the memory of a relationship, personal understanding of the properties of the object is a pointer to a memory area.

So if you want to add a dynamic property, in fact, is a dynamic association. And want to add attributes to the system class, only through the classification.

Here to add the name attribute to the NSObject, create the NSObject classification
we can use @property to add attributes to the classification

@property (nonatomic, strong) NSString *name;

Although the classification can be written @property
add attributes, but does not automatically generate the private property, will not generate a set implementation of the get method, will only generate set, get statement, we need to achieve.

Method 1: we can add attributes to the classification by using static global variables

Static NSString *_name; (void) setName: (NSString *) name {_name = name;} (NSString *) name {return};}

However, the _name static global variables are not associated with the class, regardless of the object creation and destruction, as long as the program runs in the _name variable exists, is not the real meaning of the property.

Method two: using RunTime to dynamically add attributes to
RunTime provides a way to dynamically add attributes and obtain attributes.

- (void) setName: (NSString *) name {objc_setAssociatedObject (self, @ name, name, OBJC_ASSOCIATION_RETAIN_NONATOMIC);} (NSString *) name {return objc_getAssociatedObject (self, @ @);}
  1. The dynamic properties of objc_setAssociatedObject (ID object, add const void *key, ID value, objc_AssociationPolicy Policy); ID: object: a parameter to add attributes to the object, here to add the attribute to their own, with self.
    parameters: void * two = = ID: key attribute name, according to the properties of the key object to obtain the associated values by key get attribute value and returns in objc_getAssociatedObject.
    parameter three: ID value: the associated value, that is, the value of the set method to the property to save.
    parameter four: objc_AssociationPolicy policy: policy, the attributes in what form to save. There are several
    typedef OBJC_ENUM (uintptr_t, objc_AssociationPolicy) {OBJC_ASSOCIATION_ASSIGN = 0 / /, specify a weak reference object associated with OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1, / / specify related objects strong references, non atomic OBJC_ASSOCIATION_COPY_NONATOMIC = 3, / / specify the object to be copied, non atomic OBJC_ASSOCIATION_RETAIN = 01401, / / specified objects strong reference, atomic OBJC_ASSOCIATION_COPY = 01403 / / specify the object to be copied, atomic};
  2. Get the property objc_getAssociatedObject (ID object, const void *key); parameter a: ID object: get the object which the association of attributes. Two
    parameters: void * key = = ID: what attributes, and the corresponding objc_setAssociatedObject in the key, through the key value out of value.

At this point, the name property has been successfully added to the NSObject, and the NSObject object can be assigned to the property by point syntax.

NSObject *objc [[NSObject = alloc]init]; = "xx_cc"; (@ NSLog @ "% @",;

Six. RunTime dictionary transfer model

In order to facilitate future reuse, here to add to the NSObject classification, and the implementation of the declaration of the RunTime dictionary to turn the model class.

(instancetype) modelWithDict: (NSDictionary *) Dict

First look at the difference between the KVC dictionary model and the RunTime dictionary model

KVC:KVC dictionary conversion model to achieve the principle of all Key traversal in the dictionary, and then to find the attribute name corresponding to the model, the attribute name and Key requirements must correspond to all the key dictionary must exist in the model.
RunTime:RunTime dictionary conversion model to achieve the principle of all the attribute name traversal model, and then go to the dictionary to find the corresponding Key model, it is based on the subject, what are the attributes in the model, we find those attributes in the dictionary.

The advantages of the RunTime dictionary transfer model: when the server returns too much data, and we use only a small part of it, there is no need to use an attribute that is not defined as a waste of resources. Save only the most useful attributes.

The RunTime dictionary first need to understand the process of
model, attributes defined in the class, then the class which will have a list of attributes, the attribute list exists in the form of an array, according to the list of attributes can get all the attribute name inside the class, so the traversal properties list can traverse the model of all the attribute name.
so the RunTime dictionary model process is very clear.

  1. Create model object ID objc = [[self alloc] init];
  2. Use the class_copyIvarList method to copy the attribute list unsigned int count member Ivar = 0; *ivarList = class_copyIvarList (self, & count); __unsafe_unretained Class CLS: a parameter: access to which members of the class attribute list. Here is the self, because who calls the classification method, who is self.
    : unsigned int *outCount two parameters: unsigned int pointer, unsigned int here to create count, & count is his address, guaranteed to get the count address for the count in the method of assignment. The value passed is the total number of members.
    return value: Ivar *: returns a pointer to a Ivar type. The pointer points to the zeroth element of the array, and the pointer +1 moves a byte of the Ivar unit to the higher address, which is the first element. Ivar represents member properties.
  3. Traverse the list member properties, obtain the attribute list for (int i = 0; I < count; i++) {/ / Ivar Ivar get member property = ivarList[i];}
  4. The use of ivar_getName (Ivar) for members of the attribute name, the attribute name is returned because the members of the C string and convert it to OC NSString *propertyName = [NSString stringWithUTF8String:ivar_getName (string Ivar)]; the ivar_getTypeEncoding (Ivar) could also be members of the attribute type.
  5. Because the members of the attribute name, is a member of the _ property, so it is necessary to obtain the attribute name, remove the underline, is the dictionary key. Get key NSString *key = [propertyName / substringFromIndex:1];
  6. Get the key corresponding to the Value in the dictionary. Get the value ID value / / dictionary = dict[key];
  7. To assign model attributes, and the model returns if (value) {/ / KVC assignment: not null [objc setValue:value forKey:key] return objc;}; so far has been successfully converted to the model dictionary.

Seven. RunTime dictionary conversion model of the two level conversion

In the development process is often used in the model nested, that is, there is a model of the model, try to use the RunTime model of the two conversion, the idea is relatively simple and clear.

  1. First, get the type / / member property level 1 member of the attribute of type NSString = [NSString stringWithUTF8String:ivar_getTypeEncoding *propertyType (Ivar)];
  2. It is necessary to carry out the two level conversion when the value in the first level dictionary is a dictionary, and the member attribute type in the first level model is not NSDictionary.
    value is the first to the dictionary for translation, because we usually will be transformed into the dictionary model, secondly, members attribute type is not a system, that is our custom class member properties, is two level model transformation. And when the member attribute type is NSDictionary, it shows that we want to let the member attribute is a dictionary, do not need to model conversion. ID value = dict[key]; if ([value isKindOfClass:[NSDictionary class]] & & [propertyType! ContainsString:@ “NS”]) {/ / two level conversion. }
  3. Gets the type you want to convert this model, need to do some processing on the members of the propertyType attribute type, because propertyType returns to us is @/ members attribute type “Mode/”, we need him to intercept Mode. It is important to note that this is only an escape character. @/ / “Mode/” from the front of @/ “NSRange range = [propertyType rangeOfString:@” / “[propertyType”]; propertyType = substringFromIndex:range.location + range.length]; / / Mode/ “stripped back /” range = [propertyType “/” rangeOfString:@ “]; propertyType = [propertyType substringToIndex:range.location];
  4. Gets the class object that needs to be converted. Class modelClass = NSClassFromString (propertyType);
  5. To determine if the class name is not empty then call the classification of the modelWithDict method, the value dictionary, the two level model conversion, return to the level of the model in the assignment to value. If (modelClass) {value [modelClass = modelWithDict:value];} there may be some around, re up, we are judging the value dictionary and the need for two level conversion, and then transformed into value dictionary model returns, and re assigned to value, finally to the corresponding level in the model key value model can be converted to complete the assignment the two level of the model dictionary.

Finally, a complete method of two level conversion

(instancetype) + modelWithDict: (NSDictionary *) dict{/ / 1 to create a corresponding object of class ID objc = [[self alloc] init]; / / count: unsigned int count member properties total = 0; / / get members of the attributes and the number of attributes of Ivar *ivarList members (self = class_copyIvarList, & count); for (int i = 0; I < count; i++) {/ / Ivar = ivarList[i] Ivar get member properties; / / get NSString *propertyName stringWithUTF8String:ivar_getName member name = [NSString (Ivar)]; / / get key NSString *key = [propertyName substringFromIndex:1]; / / value key: value: attribute name dictionary for dictionary value ID value = dict[key]; / / get the member attribute of type NSString *propertyTy PE = [NSString stringWithUTF8String:ivar_getTypeEncoding (Ivar)]; / / two / / value conversion value is not the attribute type member and dictionary dictionary, it needs to be converted into the model of if ([value isKindOfClass:[NSDictionary class]] & & [propertyType! ContainsString:@ "NS"]) {/ / two / two level model gets conversion type char. Convert the NSRange class name rangeOfString:@ "/" range = [propertyType "]; propertyType [propertyType = substringFromIndex:range.location + range.length]; range = [propertyType rangeOfString:@" / "[propertyType"]; propertyType = substringToIndex:range.location]; / / get to C class object class conversion Lass modelClass = NSClassFromString (propertyType); / / if the class name is not empty then two conversion of if (modelClass) {/ / return two level model assigned to value value = [modelClass modelWithDict:value] if (value);}} {/ / KVC assignment: not null [objc setValue:value forKey:key];}} / / return model of return objc;}

The above is just a simple understanding of RunTime, you can see that RunTime is very powerful. Maybe we just call a simple method, but the bottom of the system to help us do a lot of things.

If there is something wrong with the welcome. I am xx_cc, a guy who has grown up for a long time but hasn’t got enough for two.