iOS Development - Runtime


iOS Development - Runtime

> I want to conclude that Runtime is not a day or two. I have already made a manuscript in my spare time a few days ago, but I think there is still room for improvement. Now I have changed it. I will post it here to share with you. (If there are any minor problems, I hope you can point it out.) Now we will start to run Run. Time world!

Introduction to Runtime

Runtime, short for runtime, is some of the mechanisms of the system when it is running, the most important of which is the message mechanism. It is a relatively low-level pure C language API, belonging to a C language library, including many low-level C language APIs. The OC code that we usually write is actually converted to Runtime's C language code when the program runs. As follows:

// OC code:
[Person coding];

//runtime translates it into C code:
objc_msgSend(Person, @selector(coding));

II. CORRELATION FUNCTION

// Traversing through all member variables of a class
class_copyIvarList

// Traversing through all methods of a class
class_copyMethodList

// Gets a member variable with the specified name
class_getInstanceVariable

// Get the member variable name
ivar_getName

// Getting member variable type encoding
ivar_getTypeEncoding

// Get the value of an object member variable
object_getIvar

// Setting the value of an object member variable
object_setIvar

// Send messages to objects
objc_msgSend

III. Relevant Applications

  • 1. Using Runtime to traverse all attributes of model objects
  • 2. Filing and Unfiling
  • 3. Dictionary model inversion (using Runtime to traverse all attributes of the model object, according to the attribute name, the value of the pair is extracted from the dictionary and set to the attributes of the model)
  • 4. Dynamic adding/modifying attributes, dynamic adding/modifying/replacing methods (modifying Person's height and weight, replacing coding method with eating, etc.)

Demo Code

Demo Code 1: Traversing Object Properties

Create a new Person class, inherited from NSObject, and set its properties in. h file as follows

@interface NNPerson : NSObject {
    NSString * _str1;
}

@property NSString * str2;

@property (nonatomic, copy) NSString *str3;

@property (nonatomic, copy) NSDictionary * dict;

@property (nonatomic, copy) NSArray *ary;

Get its properties in ViewController, code as follows

#import "ViewController.h"
#import <objc/runtime.h>
#import "NNPerson.h"

@interface ViewController ()

@end

@implementation ViewController
- (void)viewDidLoad {
    [super viewDidLoad];

    // Get all variables in NNModel and output variable names and types
    unsigned int count = 0;
    Ivar * ivars = class_copyIvarList([NNPerson class], &count);
    for (unsigned int i = 0; i < count; i ++) {
        Ivar ivar = ivars[i];
        NSLog(@"type: %s -Name: %s ", ivar_getTypeEncoding(ivar), ivar_getName(ivar));
  }
    free(ivars);
}

Demo Code 2: Archiving and Archiving

Create a new class that inherits from NSObject. The code in.m is as follows:

#pragma mark - filing
- (void)encodeWithCoder:(NSCoder *)aCoder {
    unsigned int count = 0;

    //Get all attributes in a class
    Ivar *vars = class_copyIvarList([self class], &count);
    for (unsigned int i = 0; i < count; i ++) {
        Ivar var = vars[i];
        const char *name = ivar_getName(var);
        NSString *key = [NSString stringWithUTF8String:name];

        //KVC is used to get the value, and the corresponding value is obtained according to the attribute name.
        id value = [self valueForKey:key];
        [aCoder encodeObject:value forKey:key];
    }
    free(vars);
}


#pragma mark - Unfiling
- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder {
    if (self = [super init]) {
        unsigned int count = 0;

        //Get all attributes in a class
        Ivar *vars = class_copyIvarList([self class], &count);
        for (unsigned int i = 0; i < count; i ++) {
            Ivar var = vars[i];
            const char *name = ivar_getName(var);
            NSString *key = [NSString stringWithUTF8String:name];

            //Unarchiving
            id value = [aDecoder decodeObjectForKey:key];

             //Apportionment of Attributes by KVC
            [self setValue:value forKey:key];
        }
        free(vars);
    } 
    return self;
}

Code in ViewController:

- (void)viewDidLoad {
    [super viewDidLoad];
    LZNArchive *archive = [LZNArchive objectWithKeyValues:self.Mydictionary];
    NSString *path = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).lastObject;
    path = [path stringByAppendingPathComponent:@"hello"];
    [NSKeyedArchiver archiveRootObject:archive toFile:path];
    LZNArchive *m = [NSKeyedUnarchiver unarchiveObjectWithFile:path];
}

Example Code 3: Dictionary and Model Interchange

First, you should import a plist file to read the contents of the file into the dictionary; then you should create a new category such as NSObject+KeyValues.

#pragma mark-dictionary conversion model

+(instancetype)objectWithDict:(NSDictionary *)dict {
    id objc = [[self alloc] init];

    //Traversal of attributes in dictionaries
    for (NSString *key in dict.allKeys) {
        id value = dict[key];
        objc_property_t property = class_getProperty(self, key.UTF8String);
        unsigned int count = 0;
        objc_property_attribute_t *attributeList = property_copyAttributeList(property, &count);
        objc_property_attribute_t attribute = attributeList[0];
        NSString *string = [NSString stringWithUTF8String:attribute.value];
        if ([string isEqualToString:@"@\"LZNArchive\""]) {
            value = [self objectWithDict:value];
        }

        //Generate setter method and call it with objc_msgSend
        NSString *methodName = [NSString stringWithFormat:@"set%@%@:",[key substringToIndex:1].uppercaseString,[key substringFromIndex:1]];
        SEL setter = sel_registerName(methodName.UTF8String);
        if ([objc respondsToSelector:setter]) {
            ((void (*) (id,SEL,id)) objc_msgSend) (objc,setter,value);
        }
        free(attributeList);
    }
    return objc;
}

#pragma mark-model transfer dictionary
-(NSDictionary *)keyValuesWithObject {
    unsigned int count = 0;
    objc_property_t *propertyList = class_copyPropertyList([self class], &count);
    NSMutableDictionary *dict = [NSMutableDictionary dictionary];

    //Attributes in traversal model
    for (int i = 0; i < count; i ++) {
        objc_property_t property = propertyList[i];

        //Generate getter method and call it with objc_msgSend
        const char *propertyName = property_getName(property);
        SEL getter = sel_registerName(propertyName);
        if ([self respondsToSelector:getter]) {
            id value = ((id (*) (id,SEL)) objc_msgSend) (self,getter);

            //Judging current properties
            if ([value isKindOfClass:[self class]] && value) {
                value = [value keyValuesWithObject];
            }
            if (value) {
                NSString *key = [NSString stringWithUTF8String:propertyName];
                [dict setObject:value forKey:key];
            }
        }
    }
    free(propertyList);
    return dict;
}

Demo code 4: objc_msgSend messaging

Create a new class that inherits from NSObject, and the code in. m is as follows:

- (void)sayHello {
    NSLog(@"Hello!");
}

-(void)showName:(NSString *)name {
    NSLog(@"My name is %@",name);
}

-(void)showAge:(NSInteger)age {
    NSLog(@"My age is %ld", age);
}

-(float)showHeight{
    return 180.0f;
}

-(NSString *)showInformation {
    return @"Nice to meet you!";
}

Code in ViewController:

LZN_msgSend *msgSend = [[LZN_msgSend alloc] init];
    ((void (*) (id, SEL)) objc_msgSend) (msgSend, sel_registerName("sayHello"));

    ((void (*) (id, SEL, NSString *)) objc_msgSend) (msgSend, sel_registerName("showName:"), @"Liu Zhong Ning");

    ((void (*) (id, SEL, NSInteger)) objc_msgSend) (msgSend, sel_registerName("showAge:"), 23);

    float f = ((float (*) (id, SEL)) objc_msgSend_fpret) (msgSend, sel_registerName("showHeight"));

    NSLog(@"and height is %.2fcm",f);

    NSString *information = ((NSString* (*) (id, SEL)) objc_msgSend) (msgSend, sel_registerName("showInformation"));

    NSLog(@"%@",information);

the end

Concluding remarks: Runtime related knowledge has been sorted out, hoping to be useful to the friends you see! In addition, if demo has any problems, please let us know and make progress together! Thank you O() O ~! Have a nice weekend!

Tags: Attribute C iOS encoding

Posted on Wed, 10 Jul 2019 20:45:04 -0400 by Duswa