Introduction and Application of Runtime VIII - Dictionary Conversion Model

Step 1 of Dictionary to Model: Designing a Model

  • Model properties, usually one-to-one correspondence with key s in a dictionary
  • Question: Is it slow to generate model properties one by one?
  • Requirements: Can you automatically generate corresponding attributes from a dictionary?
  • Solution: Provide a categorization specifically to generate corresponding attribute strings from a dictionary.
    @implementation NSObject (Log)


// Print Attribute String Automatically
+ (void)resolveDict:(NSDictionary *)dict{

    // Stitching Attribute String Code
    NSMutableString *strM = [NSMutableString string];

    // 1. Walk through the dictionary, take out all key s from the dictionary, and generate corresponding attribute codes
    [dict enumerateKeysAndObjectsUsingBlock:^(id  _Nonnull key, id  _Nonnull obj, BOOL * _Nonnull stop) {

        // Type often changes, 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";
        }


        // Property String
        NSString *str;
        if ([type containsString:@"NS"]) {
            str = [NSString stringWithFormat:@"@property (nonatomic, strong) %@ *%@;",type,key];
        }else{
            str = [NSString stringWithFormat:@"@property (nonatomic, assign) %@ %@;",type,key];
        }

        // Wrap lines automatically whenever a property string is generated.
        [strM appendFormat:@"\n%@\n",str];

    }];

    // Print out the stitched strings.
    NSLog(@"%@",strM);

}


@end
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46

Step 2 of Dictionary to Model (Mode 1): How KVC does Dictionary to Model (previously used)

+ (instancetype)statusWithDict:(NSDictionary *)dict
{
    Status *status = [[self alloc] init];

    [status setValuesForKeysWithDictionary:dict];

    return status;

}

@end
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  1. Disadvantages of KVC Dictionary-to-Model: It is necessary to ensure that the attributes in the model correspond to the keys in the dictionary one-to-one.If inconsistent, [setValue:forUndefinedKey:] is called to report an error that the key cannot find.

  2. Analysis: Attributes in the model do not correspond to key s in the dictionary, and the system will call setValue:forUndefinedKey:Error.

  3. Solution: Rewrite the setValue:forUndefinedKey:, override the system's methods, and you can continue to use KVC, the dictionary to model.

Step 2 of Dictionary to Model (Mode 1): Dictionary to Model using Runtime

  1. Ideas: At run time, traverse all the attributes in the model, find the key in the dictionary according to the attribute name of the model, take out the corresponding values, and assign values to the attributes of the model.
  2. Step: Provide a NSObject classification, a specialized dictionary to model, through which all models can be converted in the future.

Implementation code:

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.

    // Parse Plist file
    NSString *filePath = [[NSBundle mainBundle] pathForResource:@"status.plist" ofType:nil];

    NSDictionary *statusDict = [NSDictionary dictionaryWithContentsOfFile:filePath];

    // Get Dictionary Array
    NSArray *dictArr = statusDict[@"statuses"];

    // Automatically generate attribute strings for models
//    [NSObject resolveDict:dictArr[0][@"user"]];


    _statuses = [NSMutableArray array];

    // Traverse 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
{
    // Idea: Walk through all the properties in the model - "Use Runtime

    // 0. Create the corresponding object
    id objc = [[self alloc] init];

    // 1. Use runtime to assign values to member attributes in objects

    // class_copyIvarList: Gets all member properties in a class
    // Ivar: The meaning of a member's attribute
    // First parameter: Indicates which class to get member properties from
    // Second parameter: Indicates how many member attributes this class has, passes in an Int variable address, and automatically assigns a value to this variable
    // Return value ivar *: refers to an array of Ivars that puts all member attributes in one array and gets them all from the returned array.
    /* Like 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;

     // Accessing the first element of an array based on a pointer
     ivarList[0];

     */
    unsigned int count;

    // Gets all member properties in a class
    Ivar *ivarList = class_copyIvarList(self, &count);

    for (int i = 0; i < count; i++) {
        // Remove the corresponding member properties from the array based on the corner label
        Ivar ivar = ivarList[i];

        // Get member property name
        NSString *name = [NSString stringWithUTF8String:ivar_getName(ivar)];

        // Processing member property names - > key s in dictionaries
        // Intercept from the first corner
        NSString *key = [name substringFromIndex:1];

        // Find the corresponding value in the dictionary based on the member property name
        id value = dict[key];

        // Secondary conversion: If there is a dictionary in the dictionary, you also need to convert the corresponding dictionary into a model
        // Judge if value is a dictionary?
        if ([value isKindOfClass:[NSDictionary class]]) {
            // Dictionary to Model
            // Get the class object of the model and call modelWithDict
            // The class name of the model is known, that is, the type of member property

            // Get member property type
           NSString *type = [NSString stringWithUTF8String:ivar_getTypeEncoding(ivar)];
          // This @'@"User\""type-"@ "User" is generated in the OC string \"->", \ is escaped and 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 to, excluding the current one
          type = [type substringToIndex:range.location];


            // Generate class object from string class name
            Class modelClass = NSClassFromString(type);


            if (modelClass) { // There is a corresponding model to rotate

                // Convert Dictionary to Model
                value  =  [modelClass modelWithDict:value];
            }


        }

        // Level 3 conversion: NSArray is also a dictionary, converting dictionaries in arrays into models.
        // Is the value an array?
        if ([value isKindOfClass:[NSArray class]]) {
            // Determine if the corresponding class has a protocol for converting dictionary arrays to model arrays
            if ([self respondsToSelector:@selector(arrayContainModelClass)]) {

                // Convert to id type to call any object's method
                id idSelf = self;

                // Get the model for the dictionary in the array
                NSString *type =  [idSelf arrayContainModelClass][key];

                // Generate model
               Class classModel = NSClassFromString(type);
                NSMutableArray *arrM = [NSMutableArray array];
                // Traverse dictionary arrays to generate model arrays
                for (NSDictionary *dict in value) {
                    // Dictionary to Model
                  id model =  [classModel modelWithDict:dict];
                    [arrM addObject:model];
                }

                // Assign model array to value
                value = arrM;

            }
        }


        if (value) { // Value is required to assign values to the properties of the model
            // Using KVC to assign values to attributes in a model
            [objc setValue:value forKey:key];
        }

    }

    return objc;
}

@end
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161

Personal summary:

To achieve automatic conversion of dictionaries and models: first, according to the implementation model of the model......

(The core is that you can iterate through each attribute in the dictionary, which is used by the Daniel Framework in json parsing, including MJEXtension,YYModel, and jsonModel, which all convert json into a dictionary and then iterate through each attribute in the dictionary to perform a modle conversion).

Basically, the mainstream json to model is indispensable. Dictionary to model substitution is performed by using the runtime method of dynamically getting attribute names of attributes. The most efficient (and time-consuming) dictionary to model is KVC. Other dictionary to model is handled by the key and value of KVC. The key and value in json are obtained dynamically. Of course, in the process of conversion, the third-party framework needs to make some blank decisions.Ah, mosaic logic, KVC to model.

Regardless of JsonModle,YYKIt,MJextension is indispensable [xx setValue:value for Key:key]; this code, if you don't trust it, can be searched, which is the core method of dictionary-to-model.

Keywords: Attribute JSON

Added by papa on Tue, 21 May 2019 19:41:41 +0300