Introduction to runtime
- RunTime is short for runtime. OC is the runtime mechanism, that is, some mechanisms at runtime, the most important of which is the message mechanism.
- For C language, the call of a function will decide which function to call when compiled.
- For OC function, it belongs to dynamic calling process. It can not decide which function to call when compiled. Only when it is really running, can it find the corresponding function to call according to the name of the function.
- Facts have proved that:
- In the compilation phase, OC can call any function, even if the function is not implemented, as long as it has been declared, no error will be reported.
- In the compilation phase, C language calls unrealized functions, which will cause errors.
II. runtime role
1. Send messages
- The essence of method invocation is to let objects send messages.
- objc_msgSend, which only objects can send messages, starts with objc.
- To use message mechanism, import #import <objc/message.h> must be imported.
- Simple use of message mechanism
// Create a person object
Person *p = [[Person alloc] init];
// Calling Object Method
[p eat];
// Essence: Let objects send messages
objc_msgSend(p, @selector(eat));
// Two ways to call class methods
// The first is called by class name
[Person eat];
// The second is called through class objects
[[Person class] eat];
// Calling class methods with class names automatically converts class names into class object calls
// Essence: Let class objects send messages
objc_msgSend([Person class], @selector(eat));
- Principle of Message Mechanisms: Implementation of Object Finding Corresponding Method Based on Method Number SEL Demapping Table
2. Exchange method
- Scenario of development and use: The method function of the system is not enough. It extends some functions to the method of the system and keeps the original function.
- Mode 1: Classes of inheritance system, rewriting method.
- Mode 2: Use runtime, exchange method.
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
// Requirement: Provide the imageNamed method with the function of judging whether the image was loaded successfully every time it was loaded.
// Step 1: First, make a classification and define an instance type image WithName:(NSString*) name that can load pictures and print them.
// Step 2: By exchanging the implementation of imageNamed and imageWithName, we can call imageWithName and indirectly invoke the implementation of imageWithName.
UIImage *image = [UIImage imageNamed:@"123"];
}
@end
@implementation UIImage (Image)
// Called when loading classified memory
+ (void)load
{
// Exchange method
// Get the imageWithName method address
Method imageWithName = class_getClassMethod(self, @selector(imageWithName:));
// Get the imageWithName method address
Method imageName = class_getClassMethod(self, @selector(imageNamed:));
// Exchange method address, equivalent to exchange implementation
method_exchangeImplementations(imageWithName, imageName);
}
// It is not possible to override the system method imageNamed in the classification because it will override the functions of the system and super cannot be invoked in the classification.
// It can load pictures and print them.
+ (instancetype)imageWithName:(NSString *)name
{
// Calling imageWithName here is equivalent to calling imageName
UIImage *image = [self imageWithName:name];
if (image == nil) {
NSLog(@"Load empty pictures");
}
return image;
}
@end
- Exchange Principle:
- Before the exchange:
- After exchange:
3. Dynamic addition method
- Scenario for development and use: If there are many class methods, loading classes into memory also consumes resources, it is necessary to generate mapping tables for each method, which can be solved by adding methods to a class dynamically.
- Simple use
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
Person *p = [[Person alloc] init];
// The default person, which does not implement the eat method, can be invoked through performSelector, but will report an error.
// Dynamic addition method will not report errors
[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 unrealized method, it calls the method processing and passes the corresponding method list.
// It just can be used to judge whether the unrealized method is the one we want to add dynamically.
+ (BOOL)resolveInstanceMethod:(SEL)sel
{
if (sel == @selector(eat)) {
// Dynamic add eat method
// First parameter: which class to add methods to
// Second parameter: method number for adding method
// Third parameter: Functional implementation of adding methods (function address)
// The fourth parameter: the type of function, (return value + parameter type) v:void @: object - & gt; self: SEL - & gt; _cmd
class_addMethod(self, @selector(eat), eat, "v@:");
}
return [super resolveInstanceMethod:sel];
}
@end
4. Adding attributes to classifications
- Principle: Declaring attributes to a class is essentially adding associations to the class, not directly adding the memory space of this value to the class memory space.
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
// Dynamic addition of attribute name to system NSObject class
NSObject *objc = [[NSObject alloc] init];
objc.name = @"Brother Xiao Biao";
NSLog(@"%@",objc.name);
}
@end
// Define the associated key
static const char *key = "name";
@implementation NSObject (Property)
- (NSString *)name
{
// Get the associated value according to the associated key.
return objc_getAssociatedObject(self, key);
}
- (void)setName:(NSString *)name
{
// First parameter: which object to add an association to
// The second parameter: the associated key, obtained by this key
// The third parameter: the associated value
// Fourth parameter: associated strategy
objc_setAssociatedObject(self, key, name, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
@end
5. Dictionary Conversion Model
- Design Model: The First Step of Dictionary-to-Dictionary Model
- Model attributes, usually one-to-one correspondence with key s in dictionaries
- Question: Is it slow to generate model attributes one by one?
- Requirement: Can you automatically generate corresponding attributes according to a dictionary?
- Solution: Provide a classification that generates the corresponding attribute string according to the dictionary.
@implementation NSObject (Log)
// Print Attribute String Automatically
+ (void)resolveDict:(NSDictionary *)dict{
// Splicing Attribute String Code
NSMutableString *strM = [NSMutableString string];
// 1. Traverse the dictionary, take out all key s in the dictionary, and generate corresponding attribute codes.
[dict enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop) {
// Types often change, pull out
NSString *type;
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];
}
// Every property string is generated, the line is wrapped automatically.
[strM appendFormat:@"\n%@\n",str];
}];
// Just print out the stitched strings.
NSLog(@"%@",strM);
}
@end
- Dictionary Transfer Model: KVC
@implementation Status
+ (instancetype)statusWithDict:(NSDictionary *)dict
{
Status *status = [[self alloc] init];
[status setValuesForKeysWithDictionary:dict];
return status;
}
@end
- Disadvantage of KVC dictionary conversion model: We must ensure that the attributes in the model correspond to the key s in the dictionary one by one.
- If inconsistent, it calls [< Status 0x7fa74b545d60> setValue: for Undefined Key:]
Wrong report key could not find.
- Analysis: If the attributes in the model do not correspond to the keys in the dictionary, the system will call setValue: for Undefined key: to report an error.
- Solution: Rewrite the setValue of the object: for Undefined Key:, override the method of the system.
We can continue to use KVC and turn the dictionary into a model.
- (void)setValue:(id)value forUndefinedKey:(NSString *)key
{
}
- Dictionary-to-model approach II: Runtime
- Idea: At runtime, all attributes in the model are traversed. According to the attribute name of the model, the key is searched in the dictionary, and the corresponding values are extracted to assign the attributes of the model.
- Step: Provide an NSObject classification, a special dictionary conversion model, after all models can be transformed through this classification.
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
// Parsing Plist files
NSString *filePath = [[NSBundle mainBundle] pathForResource:@"status.plist" ofType:nil];
NSDictionary *statusDict = [NSDictionary dictionaryWithContentsOfFile:filePath];
// Get the dictionary array
NSArray *dictArr = statusDict[@"statuses"];
// Automatic Generation of Attribute String of Model
// [NSObject resolveDict:dictArr[0][@"user"]];
_statuses = [NSMutableArray array];
// Traversing dictionary arrays
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
{
// Idea: Traverse through all attributes in the model -"Use runtime
// 0. Create the corresponding object
id objc = [[self alloc] init];
// 1. Applying runtime to assign member attributes in objects
// class_copyIvarList: Gets all member attributes in the class
// Ivar: The meaning of member attributes
// First parameter: Indicates which class member attributes to get
// Second parameter: Indicates how many member attributes this class has. Passing in an Int variable address automatically assigns a value to the variable.
// Return value ivar *: refers to an array of ivars, which places all member attributes in an array and can be obtained by returning the array.
/* This is similar to the following
Ivar ivar;
Ivar ivar1;
Ivar ivar2;
// Define an array of Ivars a
Ivar a[] = {ivar,ivar1,ivar2};
// Point an Ivar * pointer to the first element of the array
Ivar *ivarList = a;
// Access the first element of the array according to the pointer
ivarList[0];
*/
unsigned int count;
// Get all member attributes in a class
Ivar *ivarList = class_copyIvarList(self, &count);
for (int i = 0; i < count; i++) {
// According to the corner label, the corresponding member attributes are extracted from the array.
Ivar ivar = ivarList[i];
// Get the member attribute name
NSString *name = [NSString stringWithUTF8String:ivar_getName(ivar)];
// Processing member attribute name - & gt; key in dictionary
// Interception from the first corner label
NSString *key = [name substringFromIndex:1];
// Find the corresponding value in the dictionary according to the member attribute name
id value = dict[key];
// Secondary conversion: If there are dictionaries in the dictionary, the corresponding dictionary needs to be converted into a model.
// Judge whether value is a dictionary
if ([value isKindOfClass:[NSDictionary class]]) {
// Dictionary Conversion Model
// Get the class object of the model and call modelWithDict
// The class name of the model is known, which is the type of member attributes.
// Get member attribute types
NSString *type = [NSString stringWithUTF8String:ivar_getTypeEncoding(ivar)];
// Generated is this @"@" User "type -"@"User" in the OC string "-& gt;, is the meaning of escape, does not occupy characters.
// Clipping type string
NSRange range = [type rangeOfString:@"\""];
type = [type substringFromIndex:range.location + range.length];
range = [type rangeOfString:@"\""];
// Which corner label to clip, excluding the current corner label
type = [type substringToIndex:range.location];
// Generate class objects based on string class names
Class modelClass = NSClassFromString(type);
if (modelClass) { // The corresponding model needs to be transformed.
// Turn the dictionary into a model
value = [modelClass modelWithDict:value];
}
}
// Three-level conversion: NSArray is also a dictionary, which converts dictionaries in arrays into models.
// Determine whether the value is an array
if ([value isKindOfClass:[NSArray class]]) {
// Judging whether the corresponding classes have the protocol of transforming dictionary array into model array
if ([self respondsToSelector:@selector(arrayContainModelClass)]) {
// Converting to id type, you can call any object's method
id idSelf = self;
// Getting the model of dictionary correspondence in an array
NSString *type = [idSelf arrayContainModelClass][key];
// Generating model
Class classModel = NSClassFromString(type);
NSMutableArray *arrM = [NSMutableArray array];
// Traversing dictionary arrays to generate model arrays
for (NSDictionary *dict in value) {
// Dictionary Conversion Model
id model = [classModel modelWithDict:dict];
[arrM addObject:model];
}
// Assign the model array to value
value = arrM;
}
}
if (value) { // Value is required to assign values to the attributes of the model
// Applying KVC to assign attributes in the model
[objc setValue:value forKey:key];
}
}
return objc;
}
@end