Use of Runtime, use of Runtime
1. Introduction to RunTime
- RunTime is short for RunTime. OC is
Runtime Mechanism
That is, some mechanisms at runtime, the most important of which is the message mechanism.
- For C language,
Function calling determines which function to call during compilation.
.
- For OC functions
Dynamic call Process
During compilation, you cannot decide which function to call. You can only find the corresponding function to call based on the function name during actual running.
- Facts have proved that:
- In the compilation phase, OC can
Call any function
Even if this function is not implemented, no error will be reported if it is declared.
- In the compilation phase, C language calls
Unimplemented Functions
An error is reported.
Ii. Runtime function 1. Send 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.
- Use
Message Mechanism
Prerequisite: import # import <objc/message. h>
- Simple message mechanism
- Clang-rewrite-objc main. m to view the final generated code
// 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 ));
- Message Mechanism principle: Object ing table searching based on method id sel method implementation
2. Exchange Methods
- Development and application scenarios: the methods provided by the system are insufficient. Some functions are extended for the methods provided by the system and the original functions are maintained.
- Method 1: Inherit the system class and override the method.
- Method 2: Use runtime, exchange method.
@ 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
- Switching Principle:
Before exchange:
After exchange:
3. 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
4. Add attributes to a category
- Principle: To declare attributes for a class, the essence is to add associations to this class, rather than directly adding the memory space of this value to the class storage space.
@ 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
5. dictionary-to-model conversion
- Design Model: the first step of dictionary-to-model conversion
- Model attributes, usually one-to-one correspondence with the keys in the dictionary
- Q: Is it slow to generate model attributes one by one?
- Requirement: whether the corresponding attribute can be automatically generated based on a dictionary.
- Solution: A category is provided to generate the corresponding attribute string based on the dictionary.
@ Implementation NSObject (Log) // automatically print the attribute string + (void) resolveDict :( NSDictionary *) dict {// concatenate the attribute string code NSMutableString * strM = [NSMutableString string]; // 1. traverse the dictionary and obtain all the keys in the dictionary to generate the corresponding attribute code [dict enumerateKeysAndObjectsUsingBlock: ^ (id _ Nonnull key, id _ Nonnull obj, BOOL * _ Nonnull stop) {// The type changes frequently and the NSString * type is extracted. if ([obj isKindOfClass: NSClassFromString (@ "_ NSCFString")]) {type = @ "NSString ";} else if ([Obj isKindOfClass: NSClassFromString (@ "_ NSCFArray")]) {type = @ "NSArray";} else if ([obj isKindOfClass: NSClassFromString (@ "_ NSCFNumber")]) {type = @ "int";} else if ([obj isKindOfClass: NSClassFromString (@ "_ NSCFDictionary")]) {type = @ "NSDictionary";} // attribute string NSString * str; if ([type containsString: @ "NS"]) {str = [NSString stringWithFormat: @ "@ property (nonatomic, strong) % @ * % @;", type, key];} Else {str = [NSString stringWithFormat: @ "@ property (nonatomic, assign) %;", type, key] ;}// each generation of attribute strings, automatically wrap. [StrM appendFormat: @ "\ n % @ \ n", str] ;}]; // print the concatenated string. NSLog (@ "% @", strM) ;}@ end
- Dictionary-to-model Conversion Method 1: KVC
@implementation Status+ (instancetype)statusWithDict:(NSDictionary *)dict{ Status *status = [[self alloc] init]; [status setValuesForKeysWithDictionary:dict]; return status;}@end
- Disadvantages of KVC dictionary-to-model conversion: it must be ensured that the attributes in the model correspond to the keys in the dictionary one by one.
- Otherwise
[<Status 0x7fa74b545d60> setValue:forUndefinedKey:]
Reportkey
The error cannot be found.
- Analysis: the attributes in the model do not match the dictionary key one by one, and the system calls
setValue:forUndefinedKey:
Error.
- Solution: rewrite the object's
setValue:forUndefinedKey:
Overwrite the system method to continue using KVC and convert the dictionary to the model.
- (void)setValue:(id)value forUndefinedKey:(NSString *)key{}
- Dictionary-to-model Conversion Method 2: Runtime
- Idea: Use runtime to traverse all the attributes in the model, search for the key in the dictionary based on the attribute name of the model, obtain the corresponding value, and assign values to the attributes of the model.
- Step: Provide an NSObject category, which is used to convert a dictionary to a model. Later, all models can be converted through this classification.
@ Implementation ViewController-(void) viewDidLoad {[super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. // parse the Plist file NSString * filePath = [[NSBundle mainBundle] pathForResource: @ "status. plist "ofType: nil]; NSDictionary * statusDict = [NSDictionary dictionaryWithContentsOfFile: filePath]; // obtain the dictionary array NSArray * dictArr = statusDict [@" statuses "]; // automatically generate the Model Property string // [NSObject resolveDict: dictArr [0] [@ "user"]; _ statuses = [NSMutableArray array]; // traverses the dictionary array for (NSDictionary * dict in dictArr) {Status * status = [Status modelWithDict: dict]; [_ statuses addObject: status];} // test data NSLog (@ "% @", _ statuses, [_ statuses [0] user]) ;}@ end @ implementation NSObject (Model) + (instancetype) modelWithDict :( NSDictionary *) dict {// train of thought: traverse all attributes in the model-use runtime // 0. create the corresponding object id objc = [[sel F alloc] init]; // 1. use runtime to assign a value to the member attribute in the object // class_copyIvarList: Get all member attributes in the class // Ivar: Meaning of member attributes // The first parameter: indicates the member attribute in which the class is obtained. // The second parameter indicates the number of member attributes in the class. If an Int variable address is input, the variable is automatically assigned a value. // The returned value is Ivar *: it refers to an ivar array, which puts all member attributes in an array and can be obtained from all returned arrays. /* Similar to the following method: Ivar ivar; Ivar ivar1; Ivar ivar2; // defines an ivar array a Ivar a [] = {ivar, ivar1, ivar2 }; // use an Ivar * pointer to point to the first element of the array Ivar * ivarList = a; // access the first element of the array ivarList [0] based on the pointer; */unsigned int count; // obtain all member attributes in the class Ivar * ivarList = class_copyIvarList (self, & count); for (int I = 0; I <count; I ++) {// obtain the corresponding member attribute Ivar ivar = ivarList [I] From the Array Based on the badge; // obtain the member attribute name NSString * name = [NSString stringwithuf8string: ivar_getName (ivar)]; // process the member attribute name-> the key in the dictionary // extract NSString * key = [name substringFromIndex: 1] starting from the first badge; // find the corresponding value id value = dict [key] In the dictionary based on the member attribute name; // second-level conversion: If the dictionary contains a dictionary, you also need to convert the corresponding dictionary into a model // determine whether the value is a dictionary if ([value isKindOfClass: [NSDictionary class]) {// dictionary-to-model // gets the Class Object of the model. The class name that calls modelWithDict // The model is known, is the type of the member property // obtain the member property type NSString * type = [NSString stringwithuf8string: ivar_getTypeEncoding (ivar)]; // The generated @ "@ \" User \ "" type-"@" User "in the OC string \"-> ", \ is the meaning of escape, no character is occupied // The cropping type string nsange range = [type rangeOfString: @ "\" "]; type = [type substringFromIndex: range. location + range. length]; range = [type rangeOfString: @ "\" "]; // specifies the badge to be cropped, excluding the current badge type = [type substringToIndex: range. location]; // generate Class Object Class modelClass = NSClassFromString (type) based on the string Class name; if (modelClass) {// convert a dictionary to a model value = [modelClass modelWithDict: value] ;}// if a corresponding model exists, a dictionary is also used in NSArray, converts the dictionary in the array to a model. // determine whether the value is an array if ([value isKindOfClass: [NSArray class]) {// determine whether the corresponding class has the Protocol to convert the dictionary array to the model array if ([self respondsToSelector: @ selector (arrayContainModelClass)]) {// convert to id type, you can call the method id idSelf = self of any object; // obtain the model NSString * type = [idSelf arrayContainModelClass] [key] corresponding to the dictionary in the array; // generate the model Class classModel = NSClassFromString (type ); NSMutableArray * arrM = [NSMutableArray array]; // traverses the dictionary array to generate a model array for (NSDictionary * dict in value) {// dictionary-to-model id model = [classModel modelWithDict: dict]; [arrM addObject: model];} // assign the model array to value = arrM ;}} if (value) {// has a value, you need to assign a value to the model's attributes // assign a value to the attributes in the model using KVC [objc setValue: value forKey: key] ;}} return objc ;}@ end