单军华
2018-03-28 f99cf1d5cc50407394501853be06cb39f38a092c
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
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
//
//  NSObject+LHModel.m
//  LHDBDemo
//
//  Created by 3wchina01 on 16/3/21.
//  Copyright © 2016年 李浩. All rights reserved.
//
 
#import "NSObject+LHModel.h"
#import "LHObjectInfo.h"
#import <objc/message.h>
#import <UIKit/UIKit.h>
#import <objc/runtime.h>
 
@implementation NSObject (LHModel)
 
static void ModelSetValueToProperty(const void *key, const void *value, void *context)
{
    ModelSetContext* modelContext = context;
    NSString* dicKey = (__bridge NSString *)(key);
    id dicValue = (__bridge id)(value);
    LHObjectInfo* objectInfo = [((__bridge LHClassInfo*)modelContext->classInfo) objectInfoWithName:dicKey];
    NSObject* object = (__bridge NSObject*)modelContext->model;
    if (objectInfo) {
        if (objectInfo.cls) {
            setNSTypePropertyValue(object, dicValue, objectInfo.nsTypeEcoding, objectInfo.set);
            
        }else if (objectInfo.type.length>0) {
            NSNumber* number = numberWithValue(dicValue);
            setBaseTypePropertyValue(object, number, objectInfo.baseTypeEcoding,objectInfo.set);
        }
    }
}
 
static void ModelGetValueToDic(const void* key,const void* value,void* context)
{
    ModelSetContext* modelContext = context;
    NSMutableDictionary* dic = (__bridge NSMutableDictionary *)(modelContext->classInfo);
    id object = (__bridge id)(modelContext->model);
    NSString* dicKey = (__bridge NSString *)(key);
    LHObjectInfo* objectInfo = (__bridge LHObjectInfo*)(value);
    if (objectInfo) {
        if (objectInfo.cls) {
            [dic setValue:((id(*)(id,SEL))(void*) objc_msgSend)(object,objectInfo.get) forKey:dicKey];;
        }else if (objectInfo.type.length>0) {
            NSNumber* number = getBaseTypePropertyValue(object, objectInfo.baseTypeEcoding, objectInfo.get);
            [dic setValue:number forKey:dicKey];
        }
    }
}
 
static NSNumber* numberWithValue(__unsafe_unretained id value)
{
    if (!value) {
        return nil;
    }
    if ([value isKindOfClass:[NSNumber class]]) return value;
    if ([value isKindOfClass:[NSString class]]) {
        if ([value containsString:@"."]) {
            const char *cstring = ((NSString *)value).UTF8String;
            if (!cstring) return nil;
            double num = atof(cstring);
            if (isnan(num) || isinf(num)) return nil;
            return @(num);
        }else {
            const char *cstring = ((NSString*)value).UTF8String;
            if (!cstring) return nil;
            NSNumber* number = @(atoll(cstring));
            if (!atoll(cstring)) {
                number = [NSNumber numberWithChar:*(cstring+0)];
            }
            return number;
        }
    }
    return nil;
    
}
 
static void setBaseTypePropertyValue(__unsafe_unretained NSObject* object,__unsafe_unretained NSNumber* value, NSUInteger type,SEL set)
{
    switch (type) {
        case LHBaseTypeEcodingINT:
            ((void (*)(id, SEL, int))(void *) objc_msgSend)(object, set, value.intValue);
            break;
        
        case LHBaseTypeEcodingLONG:
            ((void(*)(id,SEL,long))(void*) objc_msgSend)(object,set,value.integerValue);
            break;
        case LHBaseTypeEcodingULONG:
            ((void(*)(id,SEL,long))(void*) objc_msgSend)(object,set,value.unsignedIntegerValue);
            break;
            
        case LHBaseTypeEcodingFLOAT:
            ((void(*)(id,SEL,float))(void*) objc_msgSend)(object,set,value.floatValue);
            break;
        case LHBaseTypeEcodingDOUBLE:
            ((void(*)(id,SEL,double))(void*) objc_msgSend)(object,set,value.doubleValue);
            break;
        case LHBaseTypeEcodingBOOL:
            ((void(*)(id,SEL,BOOL))(void*) objc_msgSend)(object,set,value.boolValue);
            break;
            
        case LHBaseTypeEcodingCHAR:
            ((void(*)(id,SEL,char))(void*) objc_msgSend)(object,set,value.charValue);
            break;
        default:
            ((void(*)(id,SEL,id))(void*) objc_msgSend)(object,set,nil);
            break;
    }
}
 
static void setNSTypePropertyValue(__unsafe_unretained id object,__unsafe_unretained id value,LHNSTypeEcoding typeEcoding,SEL set)
{
    switch (typeEcoding) {
        case LHNSTypeUNknow:
            ((void(*)(id,SEL,id))(void*) objc_msgSend)(object,set,value);
            break;
        
        case LHNSTypeNSString:
            if ([value isKindOfClass:[NSString class]]) {
                ((void(*)(id,SEL,id))(void*) objc_msgSend)(object,set,value);
            }else if ([value isKindOfClass:[NSNumber class]]) {
                ((void(*)(id,SEL,id))(void*) objc_msgSend)(object,set,[value stringValue]);
            }else if ([value isKindOfClass:[NSData class]]) {
                ((void(*)(id,SEL,id))(void*) objc_msgSend)(object,set,[[NSString alloc] initWithData:value encoding:NSUTF8StringEncoding]);
            }else if ([value isKindOfClass:[NSDate class]]) {
                ((void(*)(id,SEL,NSString*))(void*) objc_msgSend)(object,set,stringFormDate(value));
            }else
                ((void(*)(id,SEL,id))(void*) objc_msgSend)(object,set,value);
            break;
            
        case LHNSTypeNSNumber:
            ((void(*)(id,SEL,NSNumber*))(void*) objc_msgSend)(object,set,numberWithValue(value));
            break;
            
        case LHNSTypeNSDate:
            if ([value isKindOfClass:[NSDate class]]) {
                ((void(*)(id,SEL,NSDate*))(void*) objc_msgSend)(object,set,value);
            }else if ([value isKindOfClass:[NSString class]]) {
                ((void(*)(id,SEL,NSDate*))(void*) objc_msgSend)(object,set,dateFromString(value));
            }else if ([value isKindOfClass:[NSData class]]) {
                NSString* dateStr = [[NSString alloc] initWithData:value encoding:NSUTF8StringEncoding];
                ((void(*)(id,SEL,NSDate*))(void*) objc_msgSend)(object,set,dateFromString(dateStr));
            }else
                ((void(*)(id,SEL,id))(void*) objc_msgSend)(object,set,value);
            break;
            
        case LHNSTypeNSData:
            ((void(*)(id,SEL,NSData*))(void*) objc_msgSend)(object,set,dataFromObject(value));
            break;
            
        case LHNSTypeNSURL:
            ((void(*)(id,SEL,NSURL*))(void*) objc_msgSend)(object,set,urlFromObject(value));
            break;
            
        case LHNSTypeNSArray:
            ((void(*)(id,SEL,NSArray*))(void*) objc_msgSend)(object,set,arrayFromObject(value));
            break;
            
        case LHNSTypeNSDictionary:
            ((void(*)(id,SEL,NSDictionary*))(void*) objc_msgSend)(object,set,dicFromObject(value));
            break;
            
        case LHNSTypeUIImage:
            ((void(*)(id,SEL,UIImage*))(void*) objc_msgSend)(object,set,imageFromObject(value));
            break;
        
        default:
            break;
    }
}
 
static NSNumber* getBaseTypePropertyValue(__unsafe_unretained NSObject* object, NSUInteger type,SEL get)
{
    switch (type) {
        case LHBaseTypeEcodingINT:
            
            return @(((int (*)(id, SEL))(void *) objc_msgSend)(object, get));
            
        case LHBaseTypeEcodingLONG:
            
            return @(((long (*)(id, SEL))(void *) objc_msgSend)(object,get));
            
        case LHBaseTypeEcodingULONG:
            
            return @(((NSUInteger(*)(id,SEL))(void*) objc_msgSend)(object,get));
            
        case LHBaseTypeEcodingFLOAT:
            
            return @(((float(*)(id,SEL))(void*) objc_msgSend)(object,get));
 
        case LHBaseTypeEcodingDOUBLE:
            
            return @(((double(*)(id,SEL))(void*) objc_msgSend)(object,get));
 
        case LHBaseTypeEcodingBOOL:
            
            return @(((BOOL(*)(id,SEL))(void*) objc_msgSend)(object,get));
            
        case LHBaseTypeEcodingCHAR:
            
           return @(((char(*)(id,SEL))(void*) objc_msgSend)(object,get));
 
        default:
            return nil;
            break;
    }
}
 
typedef struct {
    void *classInfo;
    void *model;
} ModelSetContext;
 
+ (id)lh_ModelWithJSON:(id)json
{
    if (!json) return nil;
    if ([json isKindOfClass:[NSDictionary class]]) {
        return [self lh_ModelWithDictionary:(NSDictionary*)json];
    }
    NSData* jsonData;
    if ([json isKindOfClass:[NSString class]]) {
        jsonData = [(NSString*)json dataUsingEncoding:NSUTF8StringEncoding];
    }else if ([json isKindOfClass:[NSData class]])
        jsonData = json;
    if (jsonData) {
        return [self lh_ModelWithDictionary:[NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableContainers error:nil]];
    }
    return nil;
}
 
+ (id)lh_ModelWithDictionary:(NSDictionary*)dic
{
    if (!dic ||![dic isKindOfClass:[NSDictionary class]]) return nil;
    NSObject* object = [[self alloc] init];
    ModelSetContext context = {0};
    LHClassInfo* info;
    //判断缓存中是否有这个类的信息
    if ([LHClassInfo isCacheWithClass:self]) {
        info = [LHClassInfo classInfoWithClass:self];
    }else
        info = [[LHClassInfo alloc] initWithClass:self];
    context.classInfo = (__bridge void *)(info);
    context.model = (__bridge void *)(object);
    
    CFDictionaryApplyFunction((__bridge CFDictionaryRef)dic, ModelSetValueToProperty, &context);
    return object;
}
 
- (NSDictionary*)lh_ModelToDictionary
{
    if ([self isKindOfClass:[NSArray class]]) {
        return nil;
    }else if ([self isKindOfClass:[NSDictionary class]]){
        return (NSDictionary*)self;
    }else if ([self isKindOfClass:[NSString class]]||[self isKindOfClass:[NSData class]]) {
        return [NSJSONSerialization JSONObjectWithData:dataFromObject(self) options:NSJSONReadingMutableContainers error:nil];
    }else {
        NSMutableDictionary* dic = [NSMutableDictionary dictionary];
        ModelSetContext context = {0};
        context.classInfo = (__bridge void *)(dic);
        context.model = (__bridge void *)(self);
        LHClassInfo* classInfo;
        //判断缓存中是否有这个类的信息
        if ([LHClassInfo isCacheWithClass:object_getClass(self)]) {
            classInfo = [LHClassInfo classInfoWithClass:object_getClass(self)];
        }else
            classInfo = [[LHClassInfo alloc] initWithClass:object_getClass(self)];
        CFDictionaryApplyFunction((__bridge CFMutableDictionaryRef)classInfo.objectInfoDic, ModelGetValueToDic, &context);
        return dic;
    }
    return nil;
}
 
- (NSData*)lh_ModelToData
{
    return [NSJSONSerialization dataWithJSONObject:[self lh_ModelToDictionary] options:NSJSONWritingPrettyPrinted error:nil];;
}
 
+ (NSDictionary*)getAllPropertyNameAndType
{
    NSMutableDictionary* dic = [NSMutableDictionary dictionary];
    unsigned int count = 0;
    objc_property_t* property_t = class_copyPropertyList(self, &count);
    for (int i=0; i<count; i++) {
        objc_property_t propert = property_t[i];
        NSString* propertyName = [NSString stringWithUTF8String:property_getName(propert)];
        NSString* propertyType = [NSString stringWithUTF8String:property_getAttributes(propert)];
        [dic setValue:objectType(propertyType) forKey:propertyName];
    }
    free(property_t);
    return dic;
}
 
+ (NSString*)getTypeNameWith:(NSString*)propertyName
{
    NSString* typeStr = [[self getAllPropertyNameAndType]valueForKey:propertyName];
    if ([typeStr isEqualToString:@"i"]) {
        return @"INT";
    }else if ([typeStr isEqualToString:@"f"]) {
        return @"FLOAT";
    }else if ([typeStr isEqualToString:@"B"]) {
        return @"BOOL";
    }else if ([typeStr isEqualToString:@"d"]) {
        return @"DOUBLE";
    }else if ([typeStr isEqualToString:@"q"]) {
        return @"LONG";
    }else if ([typeStr isEqualToString:@"NSData"]||[typeStr isEqualToString:@"UIImage"]) {
        return @"BLOB";
    }else if ([typeStr isEqualToString:@"NSNumber"]){
        return @"INT";
    } else
        return @"TEXT";
}
 
static id objectType(NSString* typeString)
{
    if ([typeString containsString:@"@"]) {
        NSArray* strArray = [typeString componentsSeparatedByString:@"\""];
        if (strArray.count >= 1) {
            return strArray[1];
        }else
            return nil;
    }else
        return [typeString substringWithRange:NSMakeRange(1, 1)];
}
 
 
static NSString* stringFormDate(NSDate* date)
{
    NSDateFormatter* formatter = [[NSDateFormatter alloc] init];
    formatter.dateFormat = @"yyyy-MM-dd HH:mm:ss";
    return [formatter stringFromDate:date];
}
 
static NSDate* dateFromString(NSString* string)
{
    NSDateFormatter* formatter = [[NSDateFormatter alloc] init];
    formatter.dateFormat = @"yyyy-MM-dd HH:mm:ss";
    return [formatter dateFromString:string];
}
 
static NSData* dataFromObject(id object)
{
    if ([object isKindOfClass:[NSData class]]) {
        return object;
    }else if ([object isKindOfClass:[NSString class]]) {
        return [object dataUsingEncoding:NSUTF8StringEncoding];
    }else if ([object isKindOfClass:[NSDate class]]) {
        return [stringFormDate(object) dataUsingEncoding:NSUTF8StringEncoding];
    }else if ([object isKindOfClass:[NSArray class]]||[object isKindOfClass:[NSDictionary class]]) {
        return [NSJSONSerialization dataWithJSONObject:object options:NSJSONWritingPrettyPrinted error:nil];
    }else if ([object isKindOfClass:NSClassFromString(@"UIImage")]) {
        return UIImageJPEGRepresentation(object, 1);
    }else
        return [object dataUsingEncoding:NSUTF8StringEncoding];
}
 
static NSURL* urlFromObject(id object)
{
    if ([object isKindOfClass:[NSURL class]]) {
        return object;
    }else if ([object isKindOfClass:[NSString class]]) {
        return [NSURL URLWithString:[object stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
    }else  {
        return [NSURL URLWithString:[[[NSString alloc] initWithData:dataFromObject(object) encoding:NSUTF8StringEncoding] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
    }
}
 
static NSArray* arrayFromObject(id object)
{
    if ([object isKindOfClass:[NSArray class]]) {
        return object;
    }else if ([object isKindOfClass:[NSDictionary class]]){
        return nil;
    }else {
        id value = [NSJSONSerialization JSONObjectWithData:dataFromObject(object) options:NSJSONReadingMutableContainers error:nil];
        if ([value isKindOfClass:[NSArray class]]) {
            return value;
        }
        return nil;
    }
}
 
static NSDictionary* dicFromObject(id object)
{
    if ([object isKindOfClass:[NSDictionary class]]) {
        return object;
    }else {
        NSData* data = dataFromObject(object);
        id value = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:nil];
        if ([value isKindOfClass:[NSDictionary class]]) {
            return value;
        }
        return nil;
    }
}
 
static UIImage* imageFromObject(id object)
{
    if ([object isKindOfClass:[UIImage class]]) {
        return object;
    }else {
        return [UIImage imageWithData:dataFromObject(object)];
    }
}
 
@end